Code

Applying Gail's patch for font-specification
[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='3DBoxPosAngleXAction' />"
316         "    <toolitem action='3DBoxVPXAction' />"
317         "    <separator />"
318         "    <toolitem action='3DBoxPosAngleYAction' />"
319         "    <toolitem action='3DBoxVPYAction' />"
320         "    <separator />"
321         "    <toolitem action='3DBoxPosAngleZAction' />"
322         "    <toolitem action='3DBoxVPZAction' />"
323         "    <separator />"
324         "  </toolbar>"
326         "  <toolbar name='SpiralToolbar'>"
327         "    <toolitem action='SpiralStateAction' />"
328         "    <toolitem action='SpiralRevolutionAction' />"
329         "    <toolitem action='SpiralExpansionAction' />"
330         "    <toolitem action='SpiralT0Action' />"
331         "    <separator />"
332         "    <toolitem action='SpiralResetAction' />"
333         "  </toolbar>"
335         "  <toolbar name='PenToolbar'>"
336         "  </toolbar>"
338         "  <toolbar name='PencilToolbar'>"
339         "  </toolbar>"
341         "  <toolbar name='CalligraphyToolbar'>"
342         "    <separator />"
343         "    <toolitem action='CalligraphyWidthAction' />"
344         "    <toolitem action='PressureAction' />"
345         "    <toolitem action='TraceAction' />"
346         "    <toolitem action='ThinningAction' />"
347         "    <separator />"
348         "    <toolitem action='AngleAction' />"
349         "    <toolitem action='TiltAction' />"
350         "    <toolitem action='FixationAction' />"
351         "    <separator />"
352         "    <toolitem action='CapRoundingAction' />"
353         "    <separator />"
354         "    <toolitem action='TremorAction' />"
355         "    <toolitem action='WiggleAction' />"
356         "    <toolitem action='MassAction' />"
357         "    <separator />"
358         "    <toolitem action='CalligraphyResetAction' />"
359         "  </toolbar>"
361         "  <toolbar name='ArcToolbar'>"
362         "    <toolitem action='ArcStateAction' />"
363         "    <separator />"
364         "    <toolitem action='ArcStartAction' />"
365         "    <toolitem action='ArcEndAction' />"
366         "    <separator />"
367         "    <toolitem action='ArcOpenAction' />"
368         "    <separator />"
369         "    <toolitem action='ArcResetAction' />"
370         "    <separator />"
371         "  </toolbar>"
373         "  <toolbar name='PaintbucketToolbar'>"
374         "    <toolitem action='ChannelsAction' />"
375         "    <separator />"
376         "    <toolitem action='ThresholdAction' />"
377         "    <separator />"
378         "    <toolitem action='OffsetAction' />"
379         "    <toolitem action='PaintbucketUnitsAction' />"
380         "    <separator />"
381         "    <toolitem action='AutoGapAction' />"
382         "    <separator />"
383         "    <toolitem action='PaintbucketResetAction' />"
384         "  </toolbar>"
386         "  <toolbar name='DropperToolbar'>"
387         "    <toolitem action='DropperPickAlphaAction' />"
388         "    <toolitem action='DropperSetAlphaAction' />"
389         "  </toolbar>"
391         "  <toolbar name='ConnectorToolbar'>"
392         "    <toolitem action='ConnectorAvoidAction' />"
393         "    <toolitem action='ConnectorIgnoreAction' />"
394         "    <toolitem action='ConnectorSpacingAction' />"
395         "    <toolitem action='ConnectorGraphAction' />"
396         "    <toolitem action='ConnectorLengthAction' />"
397         "    <toolitem action='ConnectorDirectedAction' />"
398         "    <toolitem action='ConnectorOverlapAction' />"
399         "  </toolbar>"
401         "</ui>"
404 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
406 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
408 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
409 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
411 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
412 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
414 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
415 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
417 /* Global text entry widgets necessary for update */
418 /* GtkWidget *dropper_rgb_entry,
419           *dropper_opacity_entry ; */
420 // should be made a private member once this is converted to class
422 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
423     connection->disconnect();
424     delete connection;
427 static void purge_repr_listener( GObject* obj, GObject* tbl )
429     (void)obj;
430     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
431     if (oldrepr) { // remove old listener
432         sp_repr_remove_listener_by_data(oldrepr, tbl);
433         Inkscape::GC::release(oldrepr);
434         oldrepr = 0;
435         g_object_set_data( tbl, "repr", NULL );
436     }
439 GtkWidget *
440 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
441                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
442                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
444     SPAction *action = verb->get_action(view);
445     if (!action) return NULL;
447     SPAction *doubleclick_action;
448     if (doubleclick_verb)
449         doubleclick_action = doubleclick_verb->get_action(view);
450     else
451         doubleclick_action = NULL;
453     /* fixme: Handle sensitive/unsensitive */
454     /* fixme: Implement sp_button_new_from_action */
455     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
456     gtk_widget_show(b);
457     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
459     return b;
462 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
463                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
465     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
468 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
469                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
471     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
475 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
477     SPAction* targetAction = SP_ACTION(user_data);
478     if ( targetAction ) {
479         sp_action_perform( targetAction, NULL );
480     }
483 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
485     if ( data ) {
486         GtkAction* act = GTK_ACTION(data);
487         gtk_action_set_sensitive( act, sensitive );
488     }
491 static SPActionEventVector action_event_vector = {
492     {NULL},
493     NULL,
494     NULL,
495     sp_action_action_set_sensitive,
496     NULL,
497     NULL
498 };
500 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
502     GtkAction* act = 0;
504     SPAction* targetAction = verb->get_action(view);
505     InkAction* inky = ink_action_new( verb->get_id(), verb->get_name(), verb->get_tip(), verb->get_image(), size  );
506     act = GTK_ACTION(inky);
507     gtk_action_set_sensitive( act, targetAction->sensitive );
509     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
511     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
512     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
514     return act;
517 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
519     Inkscape::UI::View::View *view = desktop;
520     gint verbsToUse[] = {
521         // disabled until we have icons for them:
522         //find
523         //SP_VERB_EDIT_TILE,
524         //SP_VERB_EDIT_UNTILE,
525         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
526         SP_VERB_DIALOG_DISPLAY,
527         SP_VERB_DIALOG_FILL_STROKE,
528         SP_VERB_DIALOG_NAMEDVIEW,
529         SP_VERB_DIALOG_TEXT,
530         SP_VERB_DIALOG_XML_EDITOR,
531         SP_VERB_EDIT_CLONE,
532         SP_VERB_EDIT_COPY,
533         SP_VERB_EDIT_CUT,
534         SP_VERB_EDIT_DUPLICATE,
535         SP_VERB_EDIT_PASTE,
536         SP_VERB_EDIT_REDO,
537         SP_VERB_EDIT_UNDO,
538         SP_VERB_EDIT_UNLINK_CLONE,
539         SP_VERB_FILE_EXPORT,
540         SP_VERB_FILE_IMPORT,
541         SP_VERB_FILE_NEW,
542         SP_VERB_FILE_OPEN,
543         SP_VERB_FILE_PRINT,
544         SP_VERB_FILE_SAVE,
545         SP_VERB_OBJECT_TO_CURVE,
546         SP_VERB_SELECTION_GROUP,
547         SP_VERB_SELECTION_OUTLINE,
548         SP_VERB_SELECTION_UNGROUP,
549         SP_VERB_ZOOM_1_1,
550         SP_VERB_ZOOM_1_2,
551         SP_VERB_ZOOM_2_1,
552         SP_VERB_ZOOM_DRAWING,
553         SP_VERB_ZOOM_IN,
554         SP_VERB_ZOOM_NEXT,
555         SP_VERB_ZOOM_OUT,
556         SP_VERB_ZOOM_PAGE,
557         SP_VERB_ZOOM_PAGE_WIDTH,
558         SP_VERB_ZOOM_PREV,
559         SP_VERB_ZOOM_SELECTION,
560     };
562     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
563     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
565     static std::map<SPDesktop*, GtkActionGroup*> groups;
566     GtkActionGroup* mainActions = 0;
567     if ( groups.find(desktop) != groups.end() ) {
568         mainActions = groups[desktop];
569     }
571     if ( !mainActions ) {
572         mainActions = gtk_action_group_new("main");
573         groups[desktop] = mainActions;
574     }
576     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
577         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
578         if ( verb ) {
579             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
580                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
581                 gtk_action_group_add_action( mainActions, act );
582             }
583         }
584     }
586     return mainActions;
590 GtkWidget *
591 sp_tool_toolbox_new()
593     GtkTooltips *tt = gtk_tooltips_new();
594     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
596     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
597     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
599     gtk_widget_set_sensitive(tb, FALSE);
601     GtkWidget *hb = gtk_handle_box_new();
602     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
603     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
604     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
606     gtk_container_add(GTK_CONTAINER(hb), tb);
607     gtk_widget_show(GTK_WIDGET(tb));
609     sigc::connection* conn = new sigc::connection;
610     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
612     return hb;
615 static void
616 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
618     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
619     gtk_widget_queue_resize(child);
622 static void
623 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
625     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
626     gtk_widget_queue_resize(child);
629 GtkWidget *
630 sp_aux_toolbox_new()
632     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
634     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
635     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
636     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
637     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
638     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
640     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
641     g_object_set_data(G_OBJECT(tb), "top_spacer", tb_s);
643     gtk_widget_set_sensitive(tb, FALSE);
645     GtkWidget *hb = gtk_handle_box_new();
646     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
647     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
648     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
650     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
651     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
653     gtk_container_add(GTK_CONTAINER(hb), tb);
654     gtk_widget_show(GTK_WIDGET(tb));
656     sigc::connection* conn = new sigc::connection;
657     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
659     return hb;
662 //####################################
663 //# Commands Bar
664 //####################################
666 GtkWidget *
667 sp_commands_toolbox_new()
669     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
671     GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
672     GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
673     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
674     gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
675     gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
677     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
678     gtk_widget_set_sensitive(tb, FALSE);
680     GtkWidget *hb = gtk_handle_box_new();
681     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
682     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
683     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
685     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
686     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
688     gtk_container_add(GTK_CONTAINER(hb), tb);
689     gtk_widget_show(GTK_WIDGET(tb));
691     sigc::connection* conn = new sigc::connection;
692     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
694     return hb;
697 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
698                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
699                                                        gchar const *path, gchar const *data, gdouble def,
700                                                        GtkWidget *focusTarget,
701                                                        GtkWidget *us,
702                                                        GObject *dataKludge,
703                                                        gboolean altx, gchar const *altx_mark,
704                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
705                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
706                                                        void (*callback)(GtkAdjustment *, GObject *),
707                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
709     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
710                                                              lower, upper, step, page, page ) );
711     if (us) {
712         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
713     }
715     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
717     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
718     if ( shortLabel ) {
719         g_object_set( act, "short_label", shortLabel, NULL );
720     }
722     if ( (descrCount > 0) && descrLabels && descrValues ) {
723         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
724     }
726     if ( focusTarget ) {
727         ege_adjustment_action_set_focuswidget( act, focusTarget );
728     }
730     if ( altx && altx_mark ) {
731         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
732     }
734     if ( dataKludge ) {
735         g_object_set_data( dataKludge, data, adj );
736     }
738     // Using a cast just to make sure we pass in the right kind of function pointer
739     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
741     return act;
745 //####################################
746 //# node editing callbacks
747 //####################################
749 /**
750  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
751  */
752 static ShapeEditor *get_current_shape_editor()
754     if (!SP_ACTIVE_DESKTOP) {
755         return NULL;
756     }
758     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
760     if (!SP_IS_NODE_CONTEXT(event_context)) {
761         return NULL;
762     }
764     return SP_NODE_CONTEXT(event_context)->shape_editor;
768 void
769 sp_node_path_edit_add(void)
771     ShapeEditor *shape_editor = get_current_shape_editor();
772     if (shape_editor) shape_editor->add_node();
775 void
776 sp_node_path_edit_delete(void)
778     ShapeEditor *shape_editor = get_current_shape_editor();
779     if (shape_editor) shape_editor->delete_nodes();
782 void
783 sp_node_path_edit_delete_segment(void)
785     ShapeEditor *shape_editor = get_current_shape_editor();
786     if (shape_editor) shape_editor->delete_segment();
789 void
790 sp_node_path_edit_break(void)
792     ShapeEditor *shape_editor = get_current_shape_editor();
793     if (shape_editor) shape_editor->break_at_nodes();
796 void
797 sp_node_path_edit_join(void)
799     ShapeEditor *shape_editor = get_current_shape_editor();
800     if (shape_editor) shape_editor->join_nodes();
803 void
804 sp_node_path_edit_join_segment(void)
806     ShapeEditor *shape_editor = get_current_shape_editor();
807     if (shape_editor) shape_editor->join_segments();
810 void
811 sp_node_path_edit_toline(void)
813     ShapeEditor *shape_editor = get_current_shape_editor();
814     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
817 void
818 sp_node_path_edit_tocurve(void)
820     ShapeEditor *shape_editor = get_current_shape_editor();
821     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
824 void
825 sp_node_path_edit_cusp(void)
827     ShapeEditor *shape_editor = get_current_shape_editor();
828     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
831 void
832 sp_node_path_edit_smooth(void)
834     ShapeEditor *shape_editor = get_current_shape_editor();
835     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
838 void
839 sp_node_path_edit_symmetrical(void)
841     ShapeEditor *shape_editor = get_current_shape_editor();
842     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
845 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
846     bool show = gtk_toggle_action_get_active( act );
847     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
848     ShapeEditor *shape_editor = get_current_shape_editor();
849     if (shape_editor) shape_editor->show_handles(show);
852 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
853     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
856 /* is called when the node selection is modified */
857 static void
858 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
860     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
861     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
862     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
863     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
865     // quit if run by the attr_changed listener
866     if (g_object_get_data( tbl, "freeze" )) {
867         return;
868     }
870     // in turn, prevent listener from responding
871     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
873     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
874     SPUnit const *unit = tracker->getActiveUnit();
876     ShapeEditor *shape_editor = get_current_shape_editor();
877     if (shape_editor && shape_editor->has_nodepath()) {
878         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
879         int n_selected = 0;
880         if (nodepath) {
881             n_selected = nodepath->numSelected();
882         }
884         if (n_selected == 0) {
885             gtk_action_set_sensitive(xact, FALSE);
886             gtk_action_set_sensitive(yact, FALSE);
887         } else {
888             gtk_action_set_sensitive(xact, TRUE);
889             gtk_action_set_sensitive(yact, TRUE);
890             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
891             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
893             if (n_selected == 1) {
894                 NR::Point sel_node = nodepath->singleSelectedCoords();
895                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
896                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
897                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
898                 }
899             } else {
900                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
901                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
902                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
903                     /* Note: Currently x and y will always have a value, even if the coordinates of the
904                        selected nodes don't coincide (in this case we use the coordinates of the center
905                        of the bounding box). So the entries are never set to zero. */
906                     // FIXME: Maybe we should clear the entry if several nodes are selected
907                     //        instead of providing a kind of average value
908                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
909                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
910                 }
911             }
912         }
913     } else {
914         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
915         gtk_action_set_sensitive(xact, FALSE);
916         gtk_action_set_sensitive(yact, FALSE);
917     }
919     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
922 static void
923 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
925     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
927     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
928     SPUnit const *unit = tracker->getActiveUnit();
930     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
931         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
932     }
934     // quit if run by the attr_changed listener
935     if (g_object_get_data( tbl, "freeze" )) {
936         return;
937     }
939     // in turn, prevent listener from responding
940     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
942     ShapeEditor *shape_editor = get_current_shape_editor();
943     if (shape_editor && shape_editor->has_nodepath()) {
944         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
945         if (!strcmp(value_name, "x")) {
946             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
947         }
948         if (!strcmp(value_name, "y")) {
949             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
950         }
951     }
953     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
956 static void
957 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
959     sp_node_path_value_changed(adj, tbl, "x");
962 static void
963 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
965     sp_node_path_value_changed(adj, tbl, "y");
968 //################################
969 //##    Node Editing Toolbox    ##
970 //################################
972 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
974     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
975     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
976     g_object_set_data( holder, "tracker", tracker );
978     {
979         InkAction* inky = ink_action_new( "NodeInsertAction",
980                                           _("Insert node"),
981                                           _("Insert new nodes into selected segments"),
982                                           "node_insert",
983                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
984         g_object_set( inky, "short_label", _("Insert"), NULL );
985         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
986         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
987     }
989     {
990         InkAction* inky = ink_action_new( "NodeDeleteAction",
991                                           _("Delete node"),
992                                           _("Delete selected nodes"),
993                                           "node_delete",
994                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
995         g_object_set( inky, "short_label", _("Delete"), NULL );
996         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
997         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
998     }
1000     {
1001         InkAction* inky = ink_action_new( "NodeJoinAction",
1002                                           _("Join endnodes"),
1003                                           _("Join selected endnodes"),
1004                                           "node_join",
1005                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1006         g_object_set( inky, "short_label", _("Join"), NULL );
1007         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1008         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1009     }
1011     {
1012         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1013                                           _("Join Segment"),
1014                                           _("Join selected endnodes with a new segment"),
1015                                           "node_join_segment",
1016                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1017         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1018         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1019     }
1021     {
1022         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1023                                           _("Delete Segment"),
1024                                           _("Split path between two non-endpoint nodes"),
1025                                           "node_delete_segment",
1026                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1027         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1028         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1029     }
1031     {
1032         InkAction* inky = ink_action_new( "NodeBreakAction",
1033                                           _("Node Break"),
1034                                           _("Break path at selected nodes"),
1035                                           "node_break",
1036                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1037         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1038         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1039     }
1041     {
1042         InkAction* inky = ink_action_new( "NodeCuspAction",
1043                                           _("Node Cusp"),
1044                                           _("Make selected nodes corner"),
1045                                           "node_cusp",
1046                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1047         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1048         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1049     }
1051     {
1052         InkAction* inky = ink_action_new( "NodeSmoothAction",
1053                                           _("Node Smooth"),
1054                                           _("Make selected nodes smooth"),
1055                                           "node_smooth",
1056                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1057         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1058         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1059     }
1061     {
1062         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1063                                           _("Node Symmetric"),
1064                                           _("Make selected nodes symmetric"),
1065                                           "node_symmetric",
1066                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1067         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1068         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1069     }
1071     {
1072         InkAction* inky = ink_action_new( "NodeLineAction",
1073                                           _("Node Line"),
1074                                           _("Make selected segments lines"),
1075                                           "node_line",
1076                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1077         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1078         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1079     }
1081     {
1082         InkAction* inky = ink_action_new( "NodeCurveAction",
1083                                           _("Node Curve"),
1084                                           _("Make selected segments curves"),
1085                                           "node_curve",
1086                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1087         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1088         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1089     }
1091     {
1092         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1093                                                       _("Show Handles"),
1094                                                       _("Show the Bezier handles of selected nodes"),
1095                                                       "nodes_show_handles",
1096                                                       Inkscape::ICON_SIZE_DECORATION );
1097         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1098         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1099         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1100     }
1102     {
1103         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1104                                           _("Next Path Effect Parameter"),
1105                                           _("Show next Path Effect parameter for editing"),
1106                                           "edit_next_parameter",
1107                                           Inkscape::ICON_SIZE_DECORATION );
1108         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1109         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1110     }
1112     /* X coord of selected node(s) */
1113     {
1114         EgeAdjustmentAction* eact = 0;
1115         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1116         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1117         eact = create_adjustment_action( "NodeXAction",
1118                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1119                                          "tools.nodes", "Xcoord", 0,
1120                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1121                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1122                                          labels, values, G_N_ELEMENTS(labels),
1123                                          sp_node_path_x_value_changed );
1124         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1125         g_object_set_data( holder, "nodes_x_action", eact );
1126         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1127         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1128     }
1130     /* Y coord of selected node(s) */
1131     {
1132         EgeAdjustmentAction* eact = 0;
1133         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1134         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1135         eact = create_adjustment_action( "NodeYAction",
1136                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1137                                          "tools.nodes", "Ycoord", 0,
1138                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1139                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1140                                          labels, values, G_N_ELEMENTS(labels),
1141                                          sp_node_path_y_value_changed );
1142         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1143         g_object_set_data( holder, "nodes_y_action", eact );
1144         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1145         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1146     }
1148     // add the units menu
1149     {
1150         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1151         gtk_action_group_add_action( mainActions, act );
1152     }
1154     sigc::connection *connection = new sigc::connection (
1155         desktop->connectToolSubselectionChanged(sigc::bind (sigc::ptr_fun(sp_node_toolbox_coord_changed), (GObject *)holder))
1156         );
1158     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1159     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1160 } // end of sp_node_toolbox_prep()
1163 //########################
1164 //##    Zoom Toolbox    ##
1165 //########################
1167 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1169     // no custom GtkAction setup needed
1170 } // end of sp_zoom_toolbox_prep()
1172 void
1173 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1175     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")));
1179 void
1180 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1182     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")));
1185 void
1186 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1188     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")));
1191 static void
1192 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1194     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1195     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1197     if (old_desktop) {
1198         GList *children, *iter;
1200         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1201         for ( iter = children ; iter ; iter = iter->next ) {
1202             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1203         }
1204         g_list_free(children);
1205     }
1207     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1209     if (desktop) {
1210         gtk_widget_set_sensitive(toolbox, TRUE);
1211         setup_func(toolbox, desktop);
1212         update_func(desktop, desktop->event_context, toolbox);
1213         *conn = desktop->connectEventContextChanged
1214             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1215     } else {
1216         gtk_widget_set_sensitive(toolbox, FALSE);
1217     }
1219 } // end of toolbox_set_desktop()
1222 static void
1223 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1225     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1226     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1227     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1228         // "toolbox.tools" was not set. Fallback to older value
1229         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1231         // Copy the setting forwards
1232         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1233     }
1234     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1236     for (int i = 0 ; tools[i].type_name ; i++ ) {
1237         GtkWidget *button =
1238             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1239                                                               SP_BUTTON_TYPE_TOGGLE,
1240                                                               Inkscape::Verb::get(tools[i].verb),
1241                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1242                                                               desktop,
1243                                                               tooltips );
1245         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1246                            (gpointer)button );
1247     }
1251 static void
1252 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1254     gchar const *const tname = ( eventcontext
1255                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1256                                  : NULL );
1257     for (int i = 0 ; tools[i].type_name ; i++ ) {
1258         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1259         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1260     }
1263 static void
1264 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1266     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1267     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1268     GtkUIManager* mgr = gtk_ui_manager_new();
1269     GError* errVal = 0;
1270     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1271     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1273     std::map<std::string, GtkWidget*> dataHolders;
1275     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1276         if ( aux_toolboxes[i].prep_func ) {
1277             // converted to GtkActions and UIManager
1279             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1280             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1281             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1282             dataHolders[aux_toolboxes[i].type_name] = kludge;
1283             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1284         } else {
1286             GtkWidget *sub_toolbox = 0;
1287             if (aux_toolboxes[i].create_func == NULL)
1288                 sub_toolbox = sp_empty_toolbox_new(desktop);
1289             else {
1290                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1291             }
1293             gtk_size_group_add_widget( grouper, sub_toolbox );
1295             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1296             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1298         }
1299     }
1301     // Second pass to create toolbars *after* all GtkActions are created
1302     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1303         if ( aux_toolboxes[i].prep_func ) {
1304             // converted to GtkActions and UIManager
1306             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1308             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1309             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1311             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1312             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1313             g_free( tmp );
1314             tmp = 0;
1316             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1317             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1318             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1319                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1320             }
1321             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1324             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1326             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1327                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1328                 swatch->setDesktop( desktop );
1329                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1330                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1331                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1332                 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 );
1333             }
1335             gtk_widget_show_all( holder );
1336             sp_set_font_size_smaller( holder );
1338             gtk_size_group_add_widget( grouper, holder );
1340             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1341             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1342         }
1343     }
1345     g_object_unref( G_OBJECT(grouper) );
1348 static void
1349 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1351     gchar const *tname = ( eventcontext
1352                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1353                            : NULL );
1354     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1355         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1356         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1357             gtk_widget_show_all(sub_toolbox);
1358             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1359         } else {
1360             gtk_widget_hide(sub_toolbox);
1361         }
1362     }
1365 static void
1366 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1368     gchar const * descr =
1369         "<ui>"
1370         "  <toolbar name='CommandsToolbar'>"
1371         "    <toolitem action='FileNew' />"
1372         "    <toolitem action='FileOpen' />"
1373         "    <toolitem action='FileSave' />"
1374         "    <toolitem action='FilePrint' />"
1375         "    <separator />"
1376         "    <toolitem action='FileImport' />"
1377         "    <toolitem action='FileExport' />"
1378         "    <separator />"
1379         "    <toolitem action='EditUndo' />"
1380         "    <toolitem action='EditRedo' />"
1381         "    <separator />"
1382         "    <toolitem action='EditCopy' />"
1383         "    <toolitem action='EditCut' />"
1384         "    <toolitem action='EditPaste' />"
1385         "    <separator />"
1386         "    <toolitem action='ZoomSelection' />"
1387         "    <toolitem action='ZoomDrawing' />"
1388         "    <toolitem action='ZoomPage' />"
1389         "    <separator />"
1390         "    <toolitem action='EditDuplicate' />"
1391         "    <toolitem action='EditClone' />"
1392         "    <toolitem action='EditUnlinkClone' />"
1393         "    <separator />"
1394         "    <toolitem action='SelectionGroup' />"
1395         "    <toolitem action='SelectionUnGroup' />"
1396         "    <separator />"
1397         "    <toolitem action='DialogFillStroke' />"
1398         "    <toolitem action='DialogText' />"
1399         "    <toolitem action='DialogXMLEditor' />"
1400         "    <toolitem action='DialogAlignDistribute' />"
1401         "    <separator />"
1402         "    <toolitem action='DialogPreferences' />"
1403         "    <toolitem action='DialogDocumentProperties' />"
1404         "  </toolbar>"
1405         "</ui>";
1406     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1409     GtkUIManager* mgr = gtk_ui_manager_new();
1410     GError* errVal = 0;
1412     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1413     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1415     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1416     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1417         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1418     }
1419     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1420     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1421     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1424     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1427 static void
1428 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1432 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1434     gtk_widget_show(toolbox_toplevel);
1435     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1437     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1438     if (!shown_toolbox) {
1439         return;
1440     }
1441     gtk_widget_show(toolbox);
1443     // need to show the spacer, or the padding will be off
1444     GtkWidget *spacer = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "top_spacer"));
1445     gtk_widget_show(spacer);
1447     gtk_widget_show_all(shown_toolbox);
1450 void
1451 aux_toolbox_space(GtkWidget *tb, gint space)
1453     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1456 static GtkWidget *
1457 sp_empty_toolbox_new(SPDesktop *desktop)
1459     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1460     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1461     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1463     gtk_widget_show_all(tbl);
1464     sp_set_font_size_smaller (tbl);
1466     return tbl;
1469 // helper UI functions
1471 GtkWidget *
1472 sp_tb_spinbutton(
1473     gchar *label, gchar const *tooltip,
1474     gchar const *path, gchar const *data, gdouble def,
1475     GtkWidget *us,
1476     GtkWidget *tbl,
1477     gboolean altx, gchar const *altx_mark,
1478     gdouble lower, gdouble upper, gdouble step, gdouble page,
1479     void (*callback)(GtkAdjustment *, GtkWidget *),
1480     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1482     GtkTooltips *tt = gtk_tooltips_new();
1484     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1486     GtkWidget *l = gtk_label_new(label);
1487     gtk_widget_show(l);
1488     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1489     gtk_container_add(GTK_CONTAINER(hb), l);
1491     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1492                                       lower, upper, step, page, page);
1493     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1494     if (us)
1495         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1497     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1498     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1499     if (altx)
1500         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1501     gtk_widget_set_size_request(sb,
1502                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1503                                 AUX_SPINBUTTON_HEIGHT);
1504     gtk_widget_show(sb);
1505     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1506     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1507     gtk_container_add(GTK_CONTAINER(hb), sb);
1508     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1510     return hb;
1513 #define MODE_LABEL_WIDTH 70
1515 //########################
1516 //##       Star         ##
1517 //########################
1519 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1521     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1523     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1524         // do not remember prefs if this call is initiated by an undo change, because undoing object
1525         // creation sets bogus values to its attributes before it is deleted
1526         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1527     }
1529     // quit if run by the attr_changed listener
1530     if (g_object_get_data( dataKludge, "freeze" )) {
1531         return;
1532     }
1534     // in turn, prevent listener from responding
1535     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1537     bool modmade = false;
1539     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1540     GSList const *items = selection->itemList();
1541     for (; items != NULL; items = items->next) {
1542         if (SP_IS_STAR((SPItem *) items->data)) {
1543             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1544             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1545             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1546                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1547                                     + M_PI / (gint)adj->value));
1548             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1549             modmade = true;
1550         }
1551     }
1552     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1553                                    _("Star: Change number of corners"));
1555     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1558 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1560     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1562     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1563         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1564     }
1566     // quit if run by the attr_changed listener
1567     if (g_object_get_data( dataKludge, "freeze" )) {
1568         return;
1569     }
1571     // in turn, prevent listener from responding
1572     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1574     bool modmade = false;
1575     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1576     GSList const *items = selection->itemList();
1577     for (; items != NULL; items = items->next) {
1578         if (SP_IS_STAR((SPItem *) items->data)) {
1579             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1581             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1582             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1583             if (r2 < r1) {
1584                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1585             } else {
1586                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1587             }
1589             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1590             modmade = true;
1591         }
1592     }
1594     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1595                                    _("Star: Change spoke ratio"));
1597     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1600 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1602     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1603     bool flat = ege_select_one_action_get_active( act ) == 0;
1605     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1606         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1607                                     flat ? "true" : "false" );
1608     }
1610     // quit if run by the attr_changed listener
1611     if (g_object_get_data( dataKludge, "freeze" )) {
1612         return;
1613     }
1615     // in turn, prevent listener from responding
1616     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1618     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1619     GSList const *items = selection->itemList();
1620     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1621     bool modmade = false;
1623     if ( prop_action ) {
1624         gtk_action_set_sensitive( prop_action, !flat );
1625     }
1627     for (; items != NULL; items = items->next) {
1628         if (SP_IS_STAR((SPItem *) items->data)) {
1629             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1630             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1631             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1632             modmade = true;
1633         }
1634     }
1636     if (modmade) {
1637         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1638                          flat ? _("Make polygon") : _("Make star"));
1639     }
1641     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1644 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1646     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1648     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1649         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1650     }
1652     // quit if run by the attr_changed listener
1653     if (g_object_get_data( dataKludge, "freeze" )) {
1654         return;
1655     }
1657     // in turn, prevent listener from responding
1658     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1660     bool modmade = false;
1662     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1663     GSList const *items = selection->itemList();
1664     for (; items != NULL; items = items->next) {
1665         if (SP_IS_STAR((SPItem *) items->data)) {
1666             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1667             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1668             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1669             modmade = true;
1670         }
1671     }
1672     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1673                                    _("Star: Change rounding"));
1675     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1678 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1680     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1682     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1683         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1684     }
1686     // quit if run by the attr_changed listener
1687     if (g_object_get_data( dataKludge, "freeze" )) {
1688         return;
1689     }
1691     // in turn, prevent listener from responding
1692     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1694     bool modmade = false;
1696     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1697     GSList const *items = selection->itemList();
1698     for (; items != NULL; items = items->next) {
1699         if (SP_IS_STAR((SPItem *) items->data)) {
1700             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1701             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1702             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1703             modmade = true;
1704         }
1705     }
1706     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1707                                    _("Star: Change randomization"));
1709     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1713 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1714                                        gchar const */*old_value*/, gchar const */*new_value*/,
1715                                        bool /*is_interactive*/, gpointer data)
1717     GtkWidget *tbl = GTK_WIDGET(data);
1719     // quit if run by the _changed callbacks
1720     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1721         return;
1722     }
1724     // in turn, prevent callbacks from responding
1725     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1727     GtkAdjustment *adj = 0;
1729     if (!strcmp(name, "inkscape:randomized")) {
1730         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1731         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1732     } else if (!strcmp(name, "inkscape:rounded")) {
1733         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1734         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1735     } else if (!strcmp(name, "inkscape:flatsided")) {
1736         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1737         char const *flatsides = repr->attribute("inkscape:flatsided");
1738         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1739         if ( flatsides && !strcmp(flatsides,"false") ) {
1740             ege_select_one_action_set_active( flat_action, 1 );
1741             gtk_action_set_sensitive( prop_action, TRUE );
1742         } else {
1743             ege_select_one_action_set_active( flat_action, 0 );
1744             gtk_action_set_sensitive( prop_action, FALSE );
1745         }
1746     } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
1747         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1748         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1749         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1750         if (r2 < r1) {
1751             gtk_adjustment_set_value(adj, r2/r1);
1752         } else {
1753             gtk_adjustment_set_value(adj, r1/r2);
1754         }
1755     } else if (!strcmp(name, "sodipodi:sides")) {
1756         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1757         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1758     }
1760     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1764 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1766     NULL, /* child_added */
1767     NULL, /* child_removed */
1768     star_tb_event_attr_changed,
1769     NULL, /* content_changed */
1770     NULL  /* order_changed */
1771 };
1774 /**
1775  *  \param selection Should not be NULL.
1776  */
1777 static void
1778 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1780     int n_selected = 0;
1781     Inkscape::XML::Node *repr = NULL;
1783     purge_repr_listener( tbl, tbl );
1785     for (GSList const *items = selection->itemList();
1786          items != NULL;
1787          items = items->next)
1788     {
1789         if (SP_IS_STAR((SPItem *) items->data)) {
1790             n_selected++;
1791             repr = SP_OBJECT_REPR((SPItem *) items->data);
1792         }
1793     }
1795     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1797     if (n_selected == 0) {
1798         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1799     } else if (n_selected == 1) {
1800         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1802         if (repr) {
1803             g_object_set_data( tbl, "repr", repr );
1804             Inkscape::GC::anchor(repr);
1805             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1806             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1807         }
1808     } else {
1809         // FIXME: implement averaging of all parameters for multiple selected stars
1810         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1811         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1812     }
1816 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1818     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1819     // callbacks to lump all the changes for all selected objects in one undo step
1821     GtkAdjustment *adj = 0;
1823     // fixme: make settable in prefs!
1824     gint mag = 5;
1825     gdouble prop = 0.5;
1826     gboolean flat = FALSE;
1827     gdouble randomized = 0;
1828     gdouble rounded = 0;
1830     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1831     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1833     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1834     gtk_action_set_sensitive( sb2, !flat );
1836     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1837     gtk_adjustment_set_value(adj, mag);
1838     gtk_adjustment_value_changed(adj);
1840     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1841     gtk_adjustment_set_value(adj, prop);
1842     gtk_adjustment_value_changed(adj);
1844     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1845     gtk_adjustment_set_value(adj, rounded);
1846     gtk_adjustment_value_changed(adj);
1848     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1849     gtk_adjustment_set_value(adj, randomized);
1850     gtk_adjustment_value_changed(adj);
1854 void
1855 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1857     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1858     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1859     GtkWidget *l = gtk_label_new(NULL);
1860     gtk_label_set_markup(GTK_LABEL(l), title);
1861     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1862     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1863     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1867 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1869     {
1870         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1871         ege_output_action_set_use_markup( act, TRUE );
1872         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1873         g_object_set_data( holder, "mode_action", act );
1874     }
1876     {
1877         //EgeAdjustmentAction* calligraphy_angle = 0;
1878         EgeAdjustmentAction* eact = 0;
1879         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1880         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1882         /* Flatsided checkbox */
1883         {
1884             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1886             GtkTreeIter iter;
1887             gtk_list_store_append( model, &iter );
1888             gtk_list_store_set( model, &iter,
1889                                 0, _("Polygon"),
1890                                 1, _("Regular polygon (with one handle) instead of a star"),
1891                                 2, "star_flat",
1892                                 -1 );
1894             gtk_list_store_append( model, &iter );
1895             gtk_list_store_set( model, &iter,
1896                                 0, _("Star"),
1897                                 1, _("Star instead of a regular polygon (with one handle)"),
1898                                 2, "star_angled",
1899                                 -1 );
1901             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
1902             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1903             g_object_set_data( holder, "flat_action", act );
1905             ege_select_one_action_set_appearance( act, "full" );
1906             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1907             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1908             ege_select_one_action_set_icon_column( act, 2 );
1909             ege_select_one_action_set_tooltip_column( act, 1  );
1911             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1912             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1913         }
1915         /* Magnitude */
1916         {
1917         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1918         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1919         eact = create_adjustment_action( "MagnitudeAction",
1920                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
1921                                          "tools.shapes.star", "magnitude", 3,
1922                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1923                                          3, 1024, 1, 5,
1924                                          labels, values, G_N_ELEMENTS(labels),
1925                                          sp_stb_magnitude_value_changed,
1926                                          1.0, 0 );
1927         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1928         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1929         }
1931         /* Spoke ratio */
1932         {
1933         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1934         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1935         eact = create_adjustment_action( "SpokeAction",
1936                                          _("Spoke ratio"), _("Spoke ratio:"),
1937                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1938                                          // Base radius is the same for the closest handle.
1939                                          _("Base radius to tip radius ratio"),
1940                                          "tools.shapes.star", "proportion", 0.5,
1941                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1942                                          0.01, 1.0, 0.01, 0.1,
1943                                          labels, values, G_N_ELEMENTS(labels),
1944                                          sp_stb_proportion_value_changed );
1945         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1946         g_object_set_data( holder, "prop_action", eact );
1947         }
1949         if ( !isFlatSided ) {
1950             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1951         } else {
1952             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1953         }
1955         /* Roundedness */
1956         {
1957         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1958         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1959         eact = create_adjustment_action( "RoundednessAction",
1960                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1961                                          "tools.shapes.star", "rounded", 0.0,
1962                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1963                                          -10.0, 10.0, 0.01, 0.1,
1964                                          labels, values, G_N_ELEMENTS(labels),
1965                                          sp_stb_rounded_value_changed );
1966         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1967         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1968         }
1970         /* Randomization */
1971         {
1972         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1973         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1974         eact = create_adjustment_action( "RandomizationAction",
1975                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
1976                                          "tools.shapes.star", "randomized", 0.0,
1977                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1978                                          -10.0, 10.0, 0.001, 0.01,
1979                                          labels, values, G_N_ELEMENTS(labels),
1980                                          sp_stb_randomized_value_changed, 0.1, 3 );
1981         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1982         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1983         }
1984     }
1986     {
1987         /* Reset */
1988         {
1989             GtkAction* act = gtk_action_new( "StarResetAction",
1990                                              _("Defaults"),
1991                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1992                                              GTK_STOCK_CLEAR );
1993             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1994             gtk_action_group_add_action( mainActions, act );
1995             gtk_action_set_sensitive( act, TRUE );
1996         }
1997     }
1999     sigc::connection *connection = new sigc::connection(
2000         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2001         );
2002     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2003     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2007 //########################
2008 //##       Rect         ##
2009 //########################
2011 static void sp_rtb_sensitivize( GObject *tbl )
2013     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2014     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2015     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2017     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2018         gtk_action_set_sensitive( not_rounded, FALSE );
2019     } else {
2020         gtk_action_set_sensitive( not_rounded, TRUE );
2021     }
2025 static void
2026 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2027                           void (*setter)(SPRect *, gdouble))
2029     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2031     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2032     SPUnit const *unit = tracker->getActiveUnit();
2034     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2035         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2036     }
2038     // quit if run by the attr_changed listener
2039     if (g_object_get_data( tbl, "freeze" )) {
2040         return;
2041     }
2043     // in turn, prevent listener from responding
2044     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2046     bool modmade = false;
2047     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2048     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2049         if (SP_IS_RECT(items->data)) {
2050             if (adj->value != 0) {
2051                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2052             } else {
2053                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2054             }
2055             modmade = true;
2056         }
2057     }
2059     sp_rtb_sensitivize( tbl );
2061     if (modmade) {
2062         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2063                                    _("Change rectangle"));
2064     }
2066     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2069 static void
2070 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2072     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2075 static void
2076 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2078     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2081 static void
2082 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2084     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2087 static void
2088 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2090     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2095 static void
2096 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2098     GtkAdjustment *adj = 0;
2100     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2101     gtk_adjustment_set_value(adj, 0.0);
2102     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2103     gtk_adjustment_value_changed(adj);
2105     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2106     gtk_adjustment_set_value(adj, 0.0);
2107     gtk_adjustment_value_changed(adj);
2109     sp_rtb_sensitivize( obj );
2112 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2113                                        gchar const */*old_value*/, gchar const */*new_value*/,
2114                                        bool /*is_interactive*/, gpointer data)
2116     GObject *tbl = G_OBJECT(data);
2118     // quit if run by the _changed callbacks
2119     if (g_object_get_data( tbl, "freeze" )) {
2120         return;
2121     }
2123     // in turn, prevent callbacks from responding
2124     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2126     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2127     SPUnit const *unit = tracker->getActiveUnit();
2129     gpointer item = g_object_get_data( tbl, "item" );
2130     if (item && SP_IS_RECT(item)) {
2131         {
2132             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2133             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2134             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2135         }
2137         {
2138             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2139             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2140             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2141         }
2143         {
2144             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2145             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2146             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2147         }
2149         {
2150             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2151             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2152             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2153         }
2154     }
2156     sp_rtb_sensitivize( tbl );
2158     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2162 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2163     NULL, /* child_added */
2164     NULL, /* child_removed */
2165     rect_tb_event_attr_changed,
2166     NULL, /* content_changed */
2167     NULL  /* order_changed */
2168 };
2170 /**
2171  *  \param selection should not be NULL.
2172  */
2173 static void
2174 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2176     int n_selected = 0;
2177     Inkscape::XML::Node *repr = NULL;
2178     SPItem *item = NULL;
2180     if ( g_object_get_data( tbl, "repr" ) ) {
2181         g_object_set_data( tbl, "item", NULL );
2182     }
2183     purge_repr_listener( tbl, tbl );
2185     for (GSList const *items = selection->itemList();
2186          items != NULL;
2187          items = items->next) {
2188         if (SP_IS_RECT((SPItem *) items->data)) {
2189             n_selected++;
2190             item = (SPItem *) items->data;
2191             repr = SP_OBJECT_REPR(item);
2192         }
2193     }
2195     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2197     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2199     if (n_selected == 0) {
2200         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2202         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2203         gtk_action_set_sensitive(w, FALSE);
2204         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2205         gtk_action_set_sensitive(h, FALSE);
2207     } else if (n_selected == 1) {
2208         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2209         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2211         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2212         gtk_action_set_sensitive(w, TRUE);
2213         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2214         gtk_action_set_sensitive(h, TRUE);
2216         if (repr) {
2217             g_object_set_data( tbl, "repr", repr );
2218             g_object_set_data( tbl, "item", item );
2219             Inkscape::GC::anchor(repr);
2220             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2221             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2222         }
2223     } else {
2224         // FIXME: implement averaging of all parameters for multiple selected
2225         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2226         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2227         sp_rtb_sensitivize( tbl );
2228     }
2232 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2234     EgeAdjustmentAction* eact = 0;
2236     {
2237         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2238         ege_output_action_set_use_markup( act, TRUE );
2239         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2240         g_object_set_data( holder, "mode_action", act );
2241     }
2243     // rx/ry units menu: create
2244     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2245     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2246     // fixme: add % meaning per cent of the width/height
2247     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2248     g_object_set_data( holder, "tracker", tracker );
2250     /* W */
2251     {
2252         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2253         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2254         eact = create_adjustment_action( "RectWidthAction",
2255                                          _("Width"), _("W:"), _("Width of rectangle"),
2256                                          "tools.shapes.rect", "width", 0,
2257                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2258                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2259                                          labels, values, G_N_ELEMENTS(labels),
2260                                          sp_rtb_width_value_changed );
2261         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2262         g_object_set_data( holder, "width_action", eact );
2263         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2264         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2265     }
2267     /* H */
2268     {
2269         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2270         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2271         eact = create_adjustment_action( "RectHeightAction",
2272                                          _("Height"), _("H:"), _("Height of rectangle"),
2273                                          "tools.shapes.rect", "height", 0,
2274                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2275                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2276                                          labels, values, G_N_ELEMENTS(labels),
2277                                          sp_rtb_height_value_changed );
2278         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2279         g_object_set_data( holder, "height_action", eact );
2280         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2281         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2282     }
2284     /* rx */
2285     {
2286         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2287         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2288         eact = create_adjustment_action( "RadiusXAction",
2289                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2290                                          "tools.shapes.rect", "rx", 0,
2291                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2292                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2293                                          labels, values, G_N_ELEMENTS(labels),
2294                                          sp_rtb_rx_value_changed);
2295         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2296         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2297     }
2299     /* ry */
2300     {
2301         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2302         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2303         eact = create_adjustment_action( "RadiusYAction",
2304                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2305                                          "tools.shapes.rect", "ry", 0,
2306                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2307                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2308                                          labels, values, G_N_ELEMENTS(labels),
2309                                          sp_rtb_ry_value_changed);
2310         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2311         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2312     }
2314     // add the units menu
2315     {
2316         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2317         gtk_action_group_add_action( mainActions, act );
2318     }
2320     /* Reset */
2321     {
2322         InkAction* inky = ink_action_new( "RectResetAction",
2323                                           _("Not rounded"),
2324                                           _("Make corners sharp"),
2325                                           "squared_corner",
2326                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2327         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2328         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2329         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2330         g_object_set_data( holder, "not_rounded", inky );
2331     }
2333     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2334     sp_rtb_sensitivize( holder );
2336     sigc::connection *connection = new sigc::connection(
2337         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2338         );
2339     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2340     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2343 //########################
2344 //##       3D Box       ##
2345 //########################
2347 static void box3d_toggle_vp_changed (GtkToggleAction *act, GObject *dataKludge, Proj::Axis axis)
2349     SPDesktop *desktop = (SPDesktop *) g_object_get_data (dataKludge, "desktop");
2350     SPDocument *document = sp_desktop_document (desktop);
2351     // FIXME: Make sure document->current_persp3d is set correctly!
2352     Persp3D *persp = document->current_persp3d;
2354     g_return_if_fail (persp);
2356     // quit if run by the attr_changed listener
2357     if (g_object_get_data(dataKludge, "freeze")) {
2358         return;
2359     }
2361     // in turn, prevent listener from responding
2362     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
2364     persp3d_set_VP_state(persp, axis, (gtk_toggle_action_get_active(act) ? Proj::VP_INFINITE : Proj::VP_FINITE));
2366     // FIXME: Can we merge this functionality with the one in box3d_persp_tb_event_attr_changed()?
2367     gchar *str;    
2368     switch (axis) {
2369         case Proj::X:
2370             str = g_strdup ("box3d_angle_x_action");
2371             break;
2372         case Proj::Y:
2373             str = g_strdup ("box3d_angle_y_action");
2374             break;
2375         case Proj::Z:
2376             str = g_strdup ("box3d_angle_z_action");
2377             break;
2378         default:
2379             return;
2380     }
2381     GtkAction* angle_action = GTK_ACTION (g_object_get_data (dataKludge, str));
2382     if (angle_action) {
2383         gtk_action_set_sensitive (angle_action, !persp3d_VP_is_finite(persp, axis));
2384     }
2386     sp_document_maybe_done(sp_desktop_document(desktop), "toggle_vp", SP_VERB_CONTEXT_3DBOX,
2387                            _("3D Box: Toggle VP"));
2388     //sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,_("3D Box: Toggle VP"));
2390     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(FALSE));
2393 static void box3d_toggle_vp_x_changed(GtkToggleAction *act, GObject *dataKludge)
2395     box3d_toggle_vp_changed (act, dataKludge, Proj::X);
2398 static void box3d_toggle_vp_y_changed(GtkToggleAction *act, GObject *dataKludge)
2400     box3d_toggle_vp_changed (act, dataKludge, Proj::Y);
2403 static void box3d_toggle_vp_z_changed(GtkToggleAction *act, GObject *dataKludge)
2405     box3d_toggle_vp_changed (act, dataKludge, Proj::Z);
2408 static void box3d_vp_angle_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis )
2410     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
2411     Persp3D *persp = sp_desktop_document (desktop)->current_persp3d;
2413     // quit if run by the attr_changed listener
2414     if (g_object_get_data(dataKludge, "freeze")) {
2415         return;
2416     }
2418     // in turn, prevent listener from responding
2419     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
2421     if (persp) {
2422         double angle = adj->value;
2423         // FIXME: Shouldn't we set the angle via the SVG attributes of the perspective instead of directly?
2424         if (persp3d_VP_is_finite(persp, axis)) {
2425             return; 
2426         }
2427         persp->tmat.set_infinite_direction (axis, angle);
2428         persp3d_update_box_reprs (persp);
2430         sp_document_maybe_done(sp_desktop_document(desktop), "perspectiveangle", SP_VERB_CONTEXT_3DBOX,
2431                                _("3D Box: Change perspective"));
2432     }
2434     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(FALSE));
2437 static void box3d_vpx_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2439     box3d_vp_angle_changed (adj, dataKludge, Proj::X);
2442 static void box3d_vpy_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2444     box3d_vp_angle_changed (adj, dataKludge, Proj::Y);
2447 static void box3d_vpz_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2449     box3d_vp_angle_changed (adj, dataKludge, Proj::Z);
2452 // normalize angle so that it lies in the interval [0,360]
2453 static double box3d_normalize_angle (double a) {
2454     double angle = a + ((int) (a/360.0))*360;
2455     if (angle < 0) {
2456         angle += 360.0;
2457     }
2458     return angle;
2461 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2462                                               gchar const *old_value, gchar const *new_value,
2463                                               bool is_interactive, gpointer data)
2465     GtkWidget *tbl = GTK_WIDGET(data);
2467     // FIXME: if we check for "freeze" as in other tools, no action is performed at all ...
2468     // quit if run by the _changed callbacks
2469     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2470         //return;
2471     }
2473     // in turn, prevent callbacks from responding
2474     //g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2476     GtkAdjustment *adj = 0;
2477     double angle;
2478     SPDesktop *desktop = (SPDesktop *) g_object_get_data(G_OBJECT(tbl), "desktop");
2479     // FIXME: Get the persp from the box (should be the same, but ...)
2480     Persp3D *persp = sp_desktop_document (desktop)->current_persp3d;
2481     if (!strcmp(name, "inkscape:vp_x")) {
2482         GtkAction* act = GTK_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "box3d_angle_x_action"));
2483         GtkToggleAction* tact = GTK_TOGGLE_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "toggle_vp_x_action"));
2484         if (!persp3d_VP_is_finite(persp, Proj::X)) {
2485             gtk_action_set_sensitive(GTK_ACTION(act), TRUE);
2486             gtk_toggle_action_set_active(tact, TRUE);
2487         } else {
2488             gtk_action_set_sensitive(GTK_ACTION(act), FALSE);
2489             gtk_toggle_action_set_active(tact, FALSE);
2490         }
2492         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_x"));
2493         angle = persp3d_get_infinite_angle(persp, Proj::X);
2494         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2495             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2496         }
2497     }
2499     if (!strcmp(name, "inkscape:vp_y")) {
2500         GtkAction* act = GTK_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "box3d_angle_y_action"));
2501         GtkToggleAction* tact = GTK_TOGGLE_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "toggle_vp_y_action"));
2502         if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2503             gtk_action_set_sensitive(GTK_ACTION(act), TRUE);
2504             gtk_toggle_action_set_active(tact, TRUE);
2505         } else {
2506             gtk_action_set_sensitive(GTK_ACTION(act), FALSE);
2507             gtk_toggle_action_set_active(tact, FALSE);
2508         }
2510         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_y"));
2511         angle = persp3d_get_infinite_angle(persp, Proj::Y);
2512         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2513             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2514         }
2515     }
2517     if (!strcmp(name, "inkscape:vp_z")) {
2518         GtkAction* act = GTK_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "box3d_angle_z_action"));
2519         GtkToggleAction* tact = GTK_TOGGLE_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "toggle_vp_z_action"));
2520         if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2521             gtk_action_set_sensitive(GTK_ACTION(act), TRUE);
2522             gtk_toggle_action_set_active(tact, TRUE);
2523         } else {
2524             gtk_action_set_sensitive(GTK_ACTION(act), FALSE);
2525             gtk_toggle_action_set_active(tact, FALSE);
2526         }
2528         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_z"));
2529         angle = persp3d_get_infinite_angle(persp, Proj::Z);
2530         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2531             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2532         }
2533     }
2534     
2535     //g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2538 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2540     NULL, /* child_added */
2541     NULL, /* child_removed */
2542     box3d_persp_tb_event_attr_changed,
2543     NULL, /* content_changed */
2544     NULL  /* order_changed */
2545 };
2547 /**
2548  *  \param selection Should not be NULL.
2549  */
2550 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2551 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2552 static void
2553 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2555     Inkscape::XML::Node *repr = NULL;
2556     purge_repr_listener(tbl, tbl);
2558     SPItem *item = selection->singleItem();
2559     if (item && SP_IS_BOX3D(item)) {
2560         //repr = SP_OBJECT_REPR(item);
2561         repr = SP_OBJECT_REPR(SP_BOX3D(item)->persp_ref->getObject());
2562         if (repr) {
2563             g_object_set_data(tbl, "repr", repr);
2564             Inkscape::GC::anchor(repr);
2565             sp_repr_add_listener(repr, &box3d_persp_tb_repr_events, tbl);
2566             sp_repr_synthesize_events(repr, &box3d_persp_tb_repr_events, tbl);
2567         }
2568     }
2571 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2573     EgeAdjustmentAction* eact = 0;
2574     SPDocument *document = sp_desktop_document (desktop);
2575     Persp3D *persp = document->current_persp3d;
2576     bool toggled = false;
2578     /* angle of VP in X direction */
2579     eact = create_adjustment_action("3DBoxPosAngleXAction",
2580                                     _("Angle X"), _("Angle X:"), _("Angle of infinite vanishing point in X direction"),
2581                                     "tools.shapes.3dbox", "dir_vp_x", persp3d_get_infinite_angle(persp, Proj::X),
2582                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2583                                     -360.0, 360.0, 1.0, 10.0,
2584                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2585                                     box3d_vpx_angle_changed,
2586                                     0.1, 1);
2587     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2588     g_object_set_data(holder, "box3d_angle_x_action", eact);
2589     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2590         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2591     } else {
2592         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2593     }
2595     /* toggle VP in X direction */
2596     {
2597     InkToggleAction* act = ink_toggle_action_new("3DBoxVPXAction",
2598                                                   _("Toggle VP in X direction"),
2599                                                   _("Toggle VP in X direction between 'finite' and 'infinite' (= parallel)"),
2600                                                   "toggle_vp_x",
2601                                                   Inkscape::ICON_SIZE_DECORATION);
2602     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2603     g_object_set_data(holder, "toggle_vp_x_action", act);
2604     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), !persp3d_VP_is_finite(persp, Proj::X));
2605     /* we connect the signal after setting the state to avoid switching the state again */
2606     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(box3d_toggle_vp_x_changed), holder);
2607     }
2609     /* angle of VP in Y direction */
2610     eact = create_adjustment_action("3DBoxPosAngleYAction",
2611                                     _("Angle Y"), _("Angle Y:"), _("Angle of infinite vanishing point in Y direction"),
2612                                     "tools.shapes.3dbox", "dir_vp_y", persp3d_get_infinite_angle(persp, Proj::Y),
2613                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2614                                     -360.0, 360.0, 1.0, 10.0,
2615                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2616                                     box3d_vpy_angle_changed,
2617                                     0.1, 1);
2618     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2619     g_object_set_data(holder, "box3d_angle_y_action", eact);
2620     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2621         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2622     } else {
2623         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2624     }
2626     /* toggle VP in Y direction */
2627     {
2628     InkToggleAction* act = ink_toggle_action_new("3DBoxVPYAction",
2629                                                  _("Toggle VP in Y direction"),
2630                                                  _("Toggle VP in Y direction between 'finite' and 'infinite' (= parallel)"),
2631                                                  "toggle_vp_y",
2632                                                  Inkscape::ICON_SIZE_DECORATION);
2633     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2634     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), !persp3d_VP_is_finite(persp, Proj::Y));
2635     g_object_set_data(holder, "toggle_vp_y_action", act);
2636     /* we connect the signal after setting the state to avoid switching the state again */
2637     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(box3d_toggle_vp_y_changed), holder);
2638     }
2640     /* angle of VP in Z direction */
2641     eact = create_adjustment_action("3DBoxPosAngleZAction",
2642                                     _("Angle Z"), _("Angle Z:"), _("Angle of infinite vanishing point in Z direction"),
2643                                     "tools.shapes.3dbox", "dir_vp_z", persp3d_get_infinite_angle(persp, Proj::Z),
2644                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2645                                     -360.0, 360.0, 1.0, 10.0,
2646                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2647                                     box3d_vpz_angle_changed,
2648                                     0.1, 1);
2650     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2651     g_object_set_data(holder, "box3d_angle_z_action", eact);
2652     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2653         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2654     } else {
2655         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2656     }
2658     /* toggle VP in Z direction */
2659     {
2660     InkToggleAction* act = ink_toggle_action_new("3DBoxVPZAction",
2661                                                  _("Toggle VP in Z direction"),
2662                                                  _("Toggle VP in Z direction between 'finite' and 'infinite' (= parallel)"),
2663                                                  "toggle_vp_z",
2664                                                  Inkscape::ICON_SIZE_DECORATION);
2665     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2667     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), !persp3d_VP_is_finite(persp, Proj::Z));
2668     g_object_set_data(holder, "toggle_vp_z_action", act);
2669     /* we connect the signal after setting the state to avoid switching the state again */
2670     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(box3d_toggle_vp_z_changed), holder);
2671     }
2673     sigc::connection *connection = new sigc::connection(
2674         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2675        );
2676     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2677     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2680 //########################
2681 //##       Spiral       ##
2682 //########################
2684 static void
2685 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2687     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2689     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2690         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2691     }
2693     // quit if run by the attr_changed listener
2694     if (g_object_get_data( tbl, "freeze" )) {
2695         return;
2696     }
2698     // in turn, prevent listener from responding
2699     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2701     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2703     bool modmade = false;
2704     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2705          items != NULL;
2706          items = items->next)
2707     {
2708         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2709             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2710             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2711             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2712             modmade = true;
2713         }
2714     }
2716     g_free(namespaced_name);
2718     if (modmade) {
2719         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2720                                    _("Change spiral"));
2721     }
2723     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2726 static void
2727 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2729     sp_spl_tb_value_changed(adj, tbl, "revolution");
2732 static void
2733 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2735     sp_spl_tb_value_changed(adj, tbl, "expansion");
2738 static void
2739 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2741     sp_spl_tb_value_changed(adj, tbl, "t0");
2744 static void
2745 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2747     GtkWidget *tbl = GTK_WIDGET(obj);
2749     GtkAdjustment *adj;
2751     // fixme: make settable
2752     gdouble rev = 5;
2753     gdouble exp = 1.0;
2754     gdouble t0 = 0.0;
2756     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2757     gtk_adjustment_set_value(adj, rev);
2758     gtk_adjustment_value_changed(adj);
2760     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2761     gtk_adjustment_set_value(adj, exp);
2762     gtk_adjustment_value_changed(adj);
2764     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2765     gtk_adjustment_set_value(adj, t0);
2766     gtk_adjustment_value_changed(adj);
2768     spinbutton_defocus(GTK_OBJECT(tbl));
2772 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2773                                          gchar const */*old_value*/, gchar const */*new_value*/,
2774                                          bool /*is_interactive*/, gpointer data)
2776     GtkWidget *tbl = GTK_WIDGET(data);
2778     // quit if run by the _changed callbacks
2779     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2780         return;
2781     }
2783     // in turn, prevent callbacks from responding
2784     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2786     GtkAdjustment *adj;
2787     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2788     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2790     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2791     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2793     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2794     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2796     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2800 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2801     NULL, /* child_added */
2802     NULL, /* child_removed */
2803     spiral_tb_event_attr_changed,
2804     NULL, /* content_changed */
2805     NULL  /* order_changed */
2806 };
2808 static void
2809 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2811     int n_selected = 0;
2812     Inkscape::XML::Node *repr = NULL;
2814     purge_repr_listener( tbl, tbl );
2816     for (GSList const *items = selection->itemList();
2817          items != NULL;
2818          items = items->next)
2819     {
2820         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2821             n_selected++;
2822             repr = SP_OBJECT_REPR((SPItem *) items->data);
2823         }
2824     }
2826     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2828     if (n_selected == 0) {
2829         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2830     } else if (n_selected == 1) {
2831         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2833         if (repr) {
2834             g_object_set_data( tbl, "repr", repr );
2835             Inkscape::GC::anchor(repr);
2836             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2837             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2838         }
2839     } else {
2840         // FIXME: implement averaging of all parameters for multiple selected
2841         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2842         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2843     }
2847 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2849     EgeAdjustmentAction* eact = 0;
2851     {
2852         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2853         ege_output_action_set_use_markup( act, TRUE );
2854         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2855         g_object_set_data( holder, "mode_action", act );
2856     }
2858     /* Revolution */
2859     {
2860         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2861         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2862         eact = create_adjustment_action( "SpiralRevolutionAction",
2863                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
2864                                          "tools.shapes.spiral", "revolution", 3.0,
2865                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2866                                          0.01, 1024.0, 0.1, 1.0,
2867                                          labels, values, G_N_ELEMENTS(labels),
2868                                          sp_spl_tb_revolution_value_changed, 1, 2);
2869         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2870     }
2872     /* Expansion */
2873     {
2874         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2875         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2876         eact = create_adjustment_action( "SpiralExpansionAction",
2877                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2878                                          "tools.shapes.spiral", "expansion", 1.0,
2879                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2880                                          0.0, 1000.0, 0.01, 1.0,
2881                                          labels, values, G_N_ELEMENTS(labels),
2882                                          sp_spl_tb_expansion_value_changed);
2883         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2884     }
2886     /* T0 */
2887     {
2888         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2889         gdouble values[] = {0, 0.5, 0.9};
2890         eact = create_adjustment_action( "SpiralT0Action",
2891                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2892                                          "tools.shapes.spiral", "t0", 0.0,
2893                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2894                                          0.0, 0.999, 0.01, 1.0,
2895                                          labels, values, G_N_ELEMENTS(labels),
2896                                          sp_spl_tb_t0_value_changed);
2897         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2898     }
2900     /* Reset */
2901     {
2902         InkAction* inky = ink_action_new( "SpiralResetAction",
2903                                           _("Defaults"),
2904                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2905                                           GTK_STOCK_CLEAR,
2906                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2907         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2908         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2909     }
2912     sigc::connection *connection = new sigc::connection(
2913         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2914         );
2915     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2916     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2919 //########################
2920 //##     Pen/Pencil    ##
2921 //########################
2924 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2926     // Put stuff here
2929 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2931     // Put stuff here
2934 //########################
2935 //##       Tweak        ##
2936 //########################
2938 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2940     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2943 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2945     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2948 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
2950     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2953 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2955     int mode = ege_select_one_action_get_active( act );
2956     prefs_set_int_attribute("tools.tweak", "mode", mode);
2958     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2959     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2960     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2961     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2962     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2963     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2964     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
2965         if (doh) gtk_action_set_sensitive (doh, TRUE);
2966         if (dos) gtk_action_set_sensitive (dos, TRUE);
2967         if (dol) gtk_action_set_sensitive (dol, TRUE);
2968         if (doo) gtk_action_set_sensitive (doo, TRUE);
2969         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
2970         if (fid) gtk_action_set_sensitive (fid, FALSE);
2971     } else {
2972         if (doh) gtk_action_set_sensitive (doh, FALSE);
2973         if (dos) gtk_action_set_sensitive (dos, FALSE);
2974         if (dol) gtk_action_set_sensitive (dol, FALSE);
2975         if (doo) gtk_action_set_sensitive (doo, FALSE);
2976         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
2977         if (fid) gtk_action_set_sensitive (fid, TRUE);
2978     }
2981 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2983     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
2986 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
2987     bool show = gtk_toggle_action_get_active( act );
2988     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
2990 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
2991     bool show = gtk_toggle_action_get_active( act );
2992     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
2994 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
2995     bool show = gtk_toggle_action_get_active( act );
2996     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
2998 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
2999     bool show = gtk_toggle_action_get_active( act );
3000     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3003 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3005     {
3006         /* Width */
3007         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3008         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3009         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3010                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3011                                                               "tools.tweak", "width", 15,
3012                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3013                                                               1, 100, 1.0, 10.0,
3014                                                               labels, values, G_N_ELEMENTS(labels),
3015                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3016         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3017         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3018     }
3021     {
3022         /* Force */
3023         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3024         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3025         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3026                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3027                                                               "tools.tweak", "force", 20,
3028                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3029                                                               1, 100, 1.0, 10.0,
3030                                                               labels, values, G_N_ELEMENTS(labels),
3031                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3032         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3033         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3034     }
3036     /* Mode */
3037     {
3038         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3040         GtkTreeIter iter;
3041         gtk_list_store_append( model, &iter );
3042         gtk_list_store_set( model, &iter,
3043                             0, _("Push mode"),
3044                             1, _("Push parts of paths in any direction"),
3045                             2, "tweak_push_mode",
3046                             -1 );
3048         gtk_list_store_append( model, &iter );
3049         gtk_list_store_set( model, &iter,
3050                             0, _("Shrink mode"),
3051                             1, _("Shrink (inset) parts of paths"),
3052                             2, "tweak_shrink_mode",
3053                             -1 );
3055         gtk_list_store_append( model, &iter );
3056         gtk_list_store_set( model, &iter,
3057                             0, _("Grow mode"),
3058                             1, _("Grow (outset) parts of paths"),
3059                             2, "tweak_grow_mode",
3060                             -1 );
3062         gtk_list_store_append( model, &iter );
3063         gtk_list_store_set( model, &iter,
3064                             0, _("Attract mode"),
3065                             1, _("Attract parts of paths towards cursor"),
3066                             2, "tweak_attract_mode",
3067                             -1 );
3069         gtk_list_store_append( model, &iter );
3070         gtk_list_store_set( model, &iter,
3071                             0, _("Repel mode"),
3072                             1, _("Repel parts of paths from cursor"),
3073                             2, "tweak_repel_mode",
3074                             -1 );
3076         gtk_list_store_append( model, &iter );
3077         gtk_list_store_set( model, &iter,
3078                             0, _("Roughen mode"),
3079                             1, _("Roughen parts of paths"),
3080                             2, "tweak_roughen_mode",
3081                             -1 );
3083         gtk_list_store_append( model, &iter );
3084         gtk_list_store_set( model, &iter,
3085                             0, _("Color paint mode"),
3086                             1, _("Paint the tool's color upon selected objects"),
3087                             2, "tweak_colorpaint_mode",
3088                             -1 );
3090         gtk_list_store_append( model, &iter );
3091         gtk_list_store_set( model, &iter,
3092                             0, _("Color jitter mode"),
3093                             1, _("Jitter the colors of selected objects"),
3094                             2, "tweak_colorjitter_mode",
3095                             -1 );
3097         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3098         g_object_set( act, "short_label", _("Mode:"), NULL );
3099         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3100         g_object_set_data( holder, "mode_action", act );
3102         ege_select_one_action_set_appearance( act, "full" );
3103         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3104         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3105         ege_select_one_action_set_icon_column( act, 2 );
3106         ege_select_one_action_set_tooltip_column( act, 1  );
3108         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3109         ege_select_one_action_set_active( act, mode );
3110         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3112         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3113     }
3115     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3117     {
3118         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3119         ege_output_action_set_use_markup( act, TRUE );
3120         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3121         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3122             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3123         g_object_set_data( holder, "tweak_channels_label", act);
3124     }
3126     {
3127         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3128                                                       _("Hue"),
3129                                                       _("In color mode, act on objects' hue"),
3130                                                       NULL,
3131                                                       Inkscape::ICON_SIZE_DECORATION );
3132         g_object_set( act, "short_label", _("H"), NULL );
3133         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3134         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3135         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3136         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3137             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3138         g_object_set_data( holder, "tweak_doh", act);
3139     }
3140     {
3141         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3142                                                       _("Saturation"),
3143                                                       _("In color mode, act on objects' saturation"),
3144                                                       NULL,
3145                                                       Inkscape::ICON_SIZE_DECORATION );
3146         g_object_set( act, "short_label", _("S"), NULL );
3147         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3148         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3149         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3150         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3151             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3152         g_object_set_data( holder, "tweak_dos", act );
3153     }
3154     {
3155         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3156                                                       _("Lightness"),
3157                                                       _("In color mode, act on objects' lightness"),
3158                                                       NULL,
3159                                                       Inkscape::ICON_SIZE_DECORATION );
3160         g_object_set( act, "short_label", _("L"), NULL );
3161         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3162         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3163         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3164         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3165             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3166         g_object_set_data( holder, "tweak_dol", act );
3167     }
3168     {
3169         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3170                                                       _("Opacity"),
3171                                                       _("In color mode, act on objects' opacity"),
3172                                                       NULL,
3173                                                       Inkscape::ICON_SIZE_DECORATION );
3174         g_object_set( act, "short_label", _("O"), NULL );
3175         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3176         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3177         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3178         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3179             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3180         g_object_set_data( holder, "tweak_doo", act );
3181     }
3183     {   /* Fidelity */
3184         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3185         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3186         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3187                                                               _("Fidelity"), _("Fidelity:"),
3188                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3189                                                               "tools.tweak", "fidelity", 50,
3190                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3191                                                               1, 100, 1.0, 10.0,
3192                                                               labels, values, G_N_ELEMENTS(labels),
3193                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3194         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3195         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3196         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3197             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3198         g_object_set_data( holder, "tweak_fidelity", eact );
3199     }
3202     /* Use Pressure button */
3203     {
3204         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3205                                                       _("Pressure"),
3206                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3207                                                       "use_pressure",
3208                                                       Inkscape::ICON_SIZE_DECORATION );
3209         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3210         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3211         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3212     }
3217 //########################
3218 //##     Calligraphy    ##
3219 //########################
3221 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3223     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3226 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3228     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3231 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3233     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3236 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3238     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3241 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3243     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3246 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3248     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3251 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3253     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3256 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3258     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3261 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3263     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3266 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3268     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3271 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3273     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3275     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3278 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3280     // FIXME: make defaults settable via Inkscape Options
3281     struct KeyValue {
3282         char const *key;
3283         double value;
3284     } const key_values[] = {
3285         {"mass", 0.02},
3286         {"wiggle", 0.0},
3287         {"angle", 30.0},
3288         {"width", 15},
3289         {"thinning", 0.1},
3290         {"tremor", 0.0},
3291         {"flatness", 0.9},
3292         {"cap_rounding", 0.0}
3293     };
3295     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3296         KeyValue const &kv = key_values[i];
3297         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3298         if ( adj ) {
3299             gtk_adjustment_set_value(adj, kv.value);
3300         }
3301     }
3305 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3307     {
3308         EgeAdjustmentAction* calligraphy_angle = 0;
3310         {
3311         /* Width */
3312         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3313         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3314         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3315                                                               _("Pen Width"), _("Width:"),
3316                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3317                                                               "tools.calligraphic", "width", 15,
3318                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3319                                                               1, 100, 1.0, 10.0,
3320                                                               labels, values, G_N_ELEMENTS(labels),
3321                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3322         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3323         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3324         }
3326         {
3327         /* Thinning */
3328             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3329             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3330         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3331                                                               _("Stroke Thinning"), _("Thinning:"),
3332                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3333                                                               "tools.calligraphic", "thinning", 0.1,
3334                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3335                                                               -1.0, 1.0, 0.01, 0.1,
3336                                                               labels, values, G_N_ELEMENTS(labels),
3337                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3338         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3339         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3340         }
3342         {
3343         /* Angle */
3344         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3345         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3346         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3347                                                               _("Pen Angle"), _("Angle:"),
3348                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3349                                                               "tools.calligraphic", "angle", 30,
3350                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3351                                                               -90.0, 90.0, 1.0, 10.0,
3352                                                               labels, values, G_N_ELEMENTS(labels),
3353                                                               sp_ddc_angle_value_changed, 1, 0 );
3354         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3355         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3356         calligraphy_angle = eact;
3357         }
3359         {
3360         /* Fixation */
3361             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3362         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3363         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3364                                                               _("Fixation"), _("Fixation:"),
3365                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3366                                                               "tools.calligraphic", "flatness", 0.9,
3367                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3368                                                               0.0, 1.0, 0.01, 0.1,
3369                                                               labels, values, G_N_ELEMENTS(labels),
3370                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3371         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3372         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3373         }
3375         {
3376         /* Cap Rounding */
3377             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3378         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3379         // TRANSLATORS: "cap" means "end" (both start and finish) here
3380         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3381                                                               _("Cap rounding"), _("Caps:"),
3382                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3383                                                               "tools.calligraphic", "cap_rounding", 0.0,
3384                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3385                                                               0.0, 5.0, 0.01, 0.1,
3386                                                               labels, values, G_N_ELEMENTS(labels),
3387                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3388         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3389         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3390         }
3392         {
3393         /* Tremor */
3394             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3395         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3396         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3397                                                               _("Stroke Tremor"), _("Tremor:"),
3398                                                               _("Increase to make strokes rugged and trembling"),
3399                                                               "tools.calligraphic", "tremor", 0.0,
3400                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3401                                                               0.0, 1.0, 0.01, 0.1,
3402                                                               labels, values, G_N_ELEMENTS(labels),
3403                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3405         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3406         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3407         }
3409         {
3410         /* Wiggle */
3411         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3412         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3413         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3414                                                               _("Pen Wiggle"), _("Wiggle:"),
3415                                                               _("Increase to make the pen waver and wiggle"),
3416                                                               "tools.calligraphic", "wiggle", 0.0,
3417                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3418                                                               0.0, 1.0, 0.01, 0.1,
3419                                                               labels, values, G_N_ELEMENTS(labels),
3420                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3421         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3422         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3423         }
3425         {
3426         /* Mass */
3427             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3428         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3429         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3430                                                               _("Pen Mass"), _("Mass:"),
3431                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3432                                                               "tools.calligraphic", "mass", 0.02,
3433                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3434                                                               0.0, 1.0, 0.01, 0.1,
3435                                                               labels, values, G_N_ELEMENTS(labels),
3436                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3437         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3438         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3439         }
3442         /* Trace Background button */
3443         {
3444             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3445                                                           _("Trace Background"),
3446                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3447                                                           "trace_background",
3448                                                           Inkscape::ICON_SIZE_DECORATION );
3449             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3450             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3451             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3452         }
3454         /* Use Pressure button */
3455         {
3456             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3457                                                           _("Pressure"),
3458                                                           _("Use the pressure of the input device to alter the width of the pen"),
3459                                                           "use_pressure",
3460                                                           Inkscape::ICON_SIZE_DECORATION );
3461             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3462             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3463             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3464         }
3466         /* Use Tilt button */
3467         {
3468             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3469                                                           _("Tilt"),
3470                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3471                                                           "use_tilt",
3472                                                           Inkscape::ICON_SIZE_DECORATION );
3473             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3474             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3475             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3476             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3477         }
3479         /* Reset */
3480         {
3481             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3482                                              _("Defaults"),
3483                                              _("Reset all parameters to defaults"),
3484                                              GTK_STOCK_CLEAR );
3485             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3486             gtk_action_group_add_action( mainActions, act );
3487             gtk_action_set_sensitive( act, TRUE );
3488         }
3489     }
3493 //########################
3494 //##    Circle / Arc    ##
3495 //########################
3497 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3499     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3500     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3502     if (v1 == 0 && v2 == 0) {
3503         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3504             gtk_action_set_sensitive( ocb, FALSE );
3505             gtk_action_set_sensitive( make_whole, FALSE );
3506         }
3507     } else {
3508         gtk_action_set_sensitive( ocb, TRUE );
3509         gtk_action_set_sensitive( make_whole, TRUE );
3510     }
3513 static void
3514 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3516     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3518     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3519         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3520     }
3522     // quit if run by the attr_changed listener
3523     if (g_object_get_data( tbl, "freeze" )) {
3524         return;
3525     }
3527     // in turn, prevent listener from responding
3528     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3530     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3532     bool modmade = false;
3533     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3534          items != NULL;
3535          items = items->next)
3536     {
3537         SPItem *item = SP_ITEM(items->data);
3539         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3541             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3542             SPArc *arc = SP_ARC(item);
3544             if (!strcmp(value_name, "start"))
3545                 ge->start = (adj->value * M_PI)/ 180;
3546             else
3547                 ge->end = (adj->value * M_PI)/ 180;
3549             sp_genericellipse_normalize(ge);
3550             ((SPObject *)arc)->updateRepr();
3551             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3553             modmade = true;
3554         }
3555     }
3557     g_free(namespaced_name);
3559     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3561     sp_arctb_sensitivize( tbl, adj->value, other->value );
3563     if (modmade) {
3564         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3565                                    _("Arc: Change start/end"));
3566     }
3568     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3572 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3574     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3577 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3579     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3582 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3584     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3585     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3586         if ( ege_select_one_action_get_active( act ) != 0 ) {
3587             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3588         } else {
3589             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3590         }
3591     }
3593     // quit if run by the attr_changed listener
3594     if (g_object_get_data( tbl, "freeze" )) {
3595         return;
3596     }
3598     // in turn, prevent listener from responding
3599     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3601     bool modmade = false;
3603     if ( ege_select_one_action_get_active(act) != 0 ) {
3604         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3605              items != NULL;
3606              items = items->next)
3607         {
3608             if (SP_IS_ARC((SPItem *) items->data)) {
3609                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3610                 repr->setAttribute("sodipodi:open", "true");
3611                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3612                 modmade = true;
3613             }
3614         }
3615     } else {
3616         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3617              items != NULL;
3618              items = items->next)
3619         {
3620             if (SP_IS_ARC((SPItem *) items->data))    {
3621                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3622                 repr->setAttribute("sodipodi:open", NULL);
3623                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3624                 modmade = true;
3625             }
3626         }
3627     }
3629     if (modmade) {
3630         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3631                                    _("Arc: Change open/closed"));
3632     }
3634     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3637 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3639     GtkAdjustment *adj;
3640     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3641     gtk_adjustment_set_value(adj, 0.0);
3642     gtk_adjustment_value_changed(adj);
3644     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3645     gtk_adjustment_set_value(adj, 0.0);
3646     gtk_adjustment_value_changed(adj);
3648     spinbutton_defocus( GTK_OBJECT(obj) );
3651 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3652                                       gchar const */*old_value*/, gchar const */*new_value*/,
3653                                       bool /*is_interactive*/, gpointer data)
3655     GObject *tbl = G_OBJECT(data);
3657     // quit if run by the _changed callbacks
3658     if (g_object_get_data( tbl, "freeze" )) {
3659         return;
3660     }
3662     // in turn, prevent callbacks from responding
3663     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3665     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3666     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3668     GtkAdjustment *adj1,*adj2;
3669     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3670     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3671     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3672     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3674     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3676     char const *openstr = NULL;
3677     openstr = repr->attribute("sodipodi:open");
3678     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3680     if (openstr) {
3681         ege_select_one_action_set_active( ocb, 1 );
3682     } else {
3683         ege_select_one_action_set_active( ocb, 0 );
3684     }
3686     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3689 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3690     NULL, /* child_added */
3691     NULL, /* child_removed */
3692     arc_tb_event_attr_changed,
3693     NULL, /* content_changed */
3694     NULL  /* order_changed */
3695 };
3698 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3700     int n_selected = 0;
3701     Inkscape::XML::Node *repr = NULL;
3703     purge_repr_listener( tbl, tbl );
3705     for (GSList const *items = selection->itemList();
3706          items != NULL;
3707          items = items->next)
3708     {
3709         if (SP_IS_ARC((SPItem *) items->data)) {
3710             n_selected++;
3711             repr = SP_OBJECT_REPR((SPItem *) items->data);
3712         }
3713     }
3715     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3717     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3718     if (n_selected == 0) {
3719         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3720     } else if (n_selected == 1) {
3721         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3722         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3724         if (repr) {
3725             g_object_set_data( tbl, "repr", repr );
3726             Inkscape::GC::anchor(repr);
3727             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3728             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3729         }
3730     } else {
3731         // FIXME: implement averaging of all parameters for multiple selected
3732         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3733         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3734         sp_arctb_sensitivize( tbl, 1, 0 );
3735     }
3739 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3741     EgeAdjustmentAction* eact = 0;
3744     {
3745         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3746         ege_output_action_set_use_markup( act, TRUE );
3747         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3748         g_object_set_data( holder, "mode_action", act );
3749     }
3751     /* Start */
3752     {
3753         eact = create_adjustment_action( "ArcStartAction",
3754                                          _("Start"), _("Start:"),
3755                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
3756                                          "tools.shapes.arc", "start", 0.0,
3757                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3758                                          -360.0, 360.0, 1.0, 10.0,
3759                                          0, 0, 0,
3760                                          sp_arctb_start_value_changed);
3761         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3762     }
3764     /* End */
3765     {
3766         eact = create_adjustment_action( "ArcEndAction",
3767                                          _("End"), _("End:"),
3768                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
3769                                          "tools.shapes.arc", "end", 0.0,
3770                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3771                                          -360.0, 360.0, 1.0, 10.0,
3772                                          0, 0, 0,
3773                                          sp_arctb_end_value_changed);
3774         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3775     }
3777     /* Segments / Pie checkbox */
3778     {
3779         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3781         GtkTreeIter iter;
3782         gtk_list_store_append( model, &iter );
3783         gtk_list_store_set( model, &iter,
3784                             0, _("Closed arc"),
3785                             1, _("Switch to segment (closed shape with two radii)"),
3786                             2, "circle_closed_arc",
3787                             -1 );
3789         gtk_list_store_append( model, &iter );
3790         gtk_list_store_set( model, &iter,
3791                             0, _("Open Arc"),
3792                             1, _("Switch to arc (unclosed shape)"),
3793                             2, "circle_open_arc",
3794                             -1 );
3796         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3797         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3798         g_object_set_data( holder, "open_action", act );
3800         ege_select_one_action_set_appearance( act, "full" );
3801         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3802         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3803         ege_select_one_action_set_icon_column( act, 2 );
3804         ege_select_one_action_set_tooltip_column( act, 1  );
3806         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3807         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3808         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3809         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3810     }
3812     /* Make Whole */
3813     {
3814         InkAction* inky = ink_action_new( "ArcResetAction",
3815                                           _("Make whole"),
3816                                           _("Make the shape a whole ellipse, not arc or segment"),
3817                                           "reset_circle",
3818                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3819         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3820         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3821         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3822         g_object_set_data( holder, "make_whole", inky );
3823     }
3825     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3826     // sensitivize make whole and open checkbox
3827     {
3828         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3829         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3830         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3831     }
3834     sigc::connection *connection = new sigc::connection(
3835         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3836         );
3837     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3838     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3844 // toggle button callbacks and updaters
3846 //########################
3847 //##      Dropper       ##
3848 //########################
3850 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3851     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3852     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3853     if ( set_action ) {
3854         if ( gtk_toggle_action_get_active( act ) ) {
3855             gtk_action_set_sensitive( set_action, TRUE );
3856         } else {
3857             gtk_action_set_sensitive( set_action, FALSE );
3858         }
3859     }
3861     spinbutton_defocus(GTK_OBJECT(tbl));
3864 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3865     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3866     spinbutton_defocus(GTK_OBJECT(tbl));
3870 /**
3871  * Dropper auxiliary toolbar construction and setup.
3872  *
3873  * TODO: Would like to add swatch of current color.
3874  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3875  *       can drag and drop places. Will provide a nice mixing palette.
3876  */
3877 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3879     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3881     {
3882         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3883                                                       _("Pick alpha"),
3884                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3885                                                       "color_alpha_get",
3886                                                       Inkscape::ICON_SIZE_DECORATION );
3887         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3888         g_object_set_data( holder, "pick_action", act );
3889         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3890         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3891     }
3893     {
3894         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3895                                                       _("Set alpha"),
3896                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3897                                                       "color_alpha_set",
3898                                                       Inkscape::ICON_SIZE_DECORATION );
3899         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3900         g_object_set_data( holder, "set_action", act );
3901         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3902         // make sure it's disabled if we're not picking alpha
3903         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3904         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3905     }
3909 //########################
3910 //##    Text Toolbox    ##
3911 //########################
3912 /*
3913 static void
3914 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3916     //Call back for letter sizing spinbutton
3919 static void
3920 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3922     //Call back for line height spinbutton
3925 static void
3926 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3928     //Call back for horizontal kerning spinbutton
3931 static void
3932 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3934     //Call back for vertical kerning spinbutton
3937 static void
3938 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3940     //Call back for letter rotation spinbutton
3941 }*/
3943 namespace {
3945 bool visible = false;
3947 void
3948 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
3950     SPStyle *query =
3951         sp_style_new (SP_ACTIVE_DOCUMENT);
3952     
3953     int result_fontspec =
3954         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
3956     int result_family =
3957         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3959     int result_style =
3960         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3962     int result_numbers =
3963         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3965     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3967     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3968     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3969     {
3970         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3972         if (repr)
3973         {
3974             sp_style_read_from_repr (query, repr);
3975         }
3976         else
3977         {
3978             return;
3979         }
3980     }
3982     if (query->text)
3983     {
3984         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
3985             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3986             gtk_entry_set_text (GTK_ENTRY (entry), "");
3988         } else if (query->text->font_specification.value || query->text->font_family.value) {
3990             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3991             
3992             // Get the font that corresponds 
3993             Glib::ustring familyName;
3994             
3995             font_instance * font = font_factory::Default()->FaceFromStyle(query);
3996             if (font) {
3997                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
3998                 font->Unref();
3999                 font = NULL;
4000             }
4001             
4002             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4004             Gtk::TreePath path;
4005             try {
4006                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4007             } catch (...) {
4008                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4009                 return;
4010             }
4012             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4013             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4015             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4017             gtk_tree_selection_select_path (tselection, path.gobj());
4018             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4020             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4021         }
4023         //Size
4024         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4025         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4026         g_object_set_data (tbl, "size-block", gpointer(1));
4027         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4028         g_object_set_data (tbl, "size-block", gpointer(0));
4029         free (str);
4031         //Anchor
4032         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4033         {
4034             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4035             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4036             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4037             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4038         }
4039         else
4040         {
4041             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4042             {
4043                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4044                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4045                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4046                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4047             }
4048             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4049             {
4050                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4051                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4052                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4053                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4054             }
4055             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4056             {
4057                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4058                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4059                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4060                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4061             }
4062         }
4064         //Style
4065         {
4066             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4068             gboolean active = gtk_toggle_button_get_active (button);
4069             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4071             if (active != check)
4072             {
4073                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4074                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4075                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4076             }
4077         }
4079         {
4080             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4082             gboolean active = gtk_toggle_button_get_active (button);
4083             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4085             if (active != check)
4086             {
4087                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4088                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4089                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4090             }
4091         }
4093         //Orientation
4094         //locking both buttons, changing one affect all group (both)
4095         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4096         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4098         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4099         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4101         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4102         {
4103             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4104         }
4105         else
4106         {
4107             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4108         }
4109         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4110         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4111     }
4113     sp_style_unref(query);
4116 void
4117 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4119     sp_text_toolbox_selection_changed (selection, tbl);
4122 void
4123 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4125     sp_text_toolbox_selection_changed (NULL, tbl);
4128 void
4129 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4130                                 GObject             *tbl)
4132     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4133     GtkTreeModel *model = 0;
4134     GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4135     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4136     GtkTreeIter   iter;
4137     char         *family = 0;
4139     (void)popdown;
4141     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4142     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4144     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4145         return;
4146     }
4148     gtk_tree_model_get (model, &iter, 0, &family, -1);
4150     if (g_object_get_data (G_OBJECT (selection), "block"))
4151     {
4152         gtk_entry_set_text (GTK_ENTRY (entry), family);
4153         return;
4154     }
4156     gtk_entry_set_text (GTK_ENTRY (entry), family);
4158     SPStyle *query =
4159         sp_style_new (SP_ACTIVE_DOCUMENT);
4161     int result_fontspec =
4162         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4163     
4164     SPCSSAttr *css = sp_repr_css_attr_new ();
4165     
4166     std::string fontSpec = query->text->font_specification.value;
4167     if (!fontSpec.empty()) {
4168         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4169         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4170             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4171             if (font) {
4172                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4173                 
4174                 // Set all the these just in case they were altered when finding the best
4175                 // match for the new family and old style...
4176                 
4177                 gchar c[256];
4178                 
4179                 font->Family(c, 256);
4180                 sp_repr_css_set_property (css, "font-family", c);
4181                 
4182                 font->Attribute( "weight", c, 256);
4183                 sp_repr_css_set_property (css, "font-weight", c);
4184                 
4185                 font->Attribute("style", c, 256);
4186                 sp_repr_css_set_property (css, "font-style", c);
4187                 
4188                 font->Attribute("stretch", c, 256);
4189                 sp_repr_css_set_property (css, "font-stretch", c);
4190                 
4191                 font->Attribute("variant", c, 256);
4192                 sp_repr_css_set_property (css, "font-variant", c);
4193                 
4194                 font->Unref();
4195             }
4196         }
4197     }
4199     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4200     if (result_fontspec == QUERY_STYLE_NOTHING)
4201     {
4202         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4203         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4204     }
4205     else
4206     {
4207         sp_desktop_set_style (desktop, css, true, true);
4208     }
4210     sp_style_unref(query);
4212     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4213                                    _("Text: Change font family"));
4214     sp_repr_css_attr_unref (css);
4215     free (family);
4216     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4218     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4221 void
4222 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4223                                        GObject      *tbl)
4225     const char *family = gtk_entry_get_text (entry);
4227     try {
4228         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4229         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4230         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4231         gtk_tree_selection_select_path (selection, path.gobj());
4232         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4233         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4234     } catch (...) {
4235         if (family && strlen (family))
4236         {
4237             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4238         }
4239     }
4242 void
4243 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4244                                    gpointer          data)
4246     if (g_object_get_data (G_OBJECT (button), "block")) return;
4247     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4248     int prop = GPOINTER_TO_INT(data);
4250     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4251     SPCSSAttr *css = sp_repr_css_attr_new ();
4253     switch (prop)
4254     {
4255         case 0:
4256         {
4257             sp_repr_css_set_property (css, "text-anchor", "start");
4258             sp_repr_css_set_property (css, "text-align", "start");
4259             break;
4260         }
4261         case 1:
4262         {
4263             sp_repr_css_set_property (css, "text-anchor", "middle");
4264             sp_repr_css_set_property (css, "text-align", "center");
4265             break;
4266         }
4268         case 2:
4269         {
4270             sp_repr_css_set_property (css, "text-anchor", "end");
4271             sp_repr_css_set_property (css, "text-align", "end");
4272             break;
4273         }
4275         case 3:
4276         {
4277             sp_repr_css_set_property (css, "text-anchor", "start");
4278             sp_repr_css_set_property (css, "text-align", "justify");
4279             break;
4280         }
4281     }
4283     SPStyle *query =
4284         sp_style_new (SP_ACTIVE_DOCUMENT);
4285     int result_numbers =
4286         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4288     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4289     if (result_numbers == QUERY_STYLE_NOTHING)
4290     {
4291         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4292     }
4294     sp_style_unref(query);
4296     sp_desktop_set_style (desktop, css, true, true);
4297     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4298                                    _("Text: Change alignment"));
4299     sp_repr_css_attr_unref (css);
4301     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4304 void
4305 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4306                                gpointer          data)
4308     if (g_object_get_data (G_OBJECT (button), "block")) return;
4310     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4311     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4312     int          prop       = GPOINTER_TO_INT(data);
4313     bool         active     = gtk_toggle_button_get_active (button);
4315     SPStyle *query =
4316         sp_style_new (SP_ACTIVE_DOCUMENT);
4317     int result_fontspec =
4318         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4319     
4320     Glib::ustring fontSpec = query->text->font_specification.value;
4321     Glib::ustring newFontSpec;
4323     switch (prop)
4324     {
4325         case 0:
4326         {
4327             if (!fontSpec.empty()) {
4328                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4329             }
4330             if (fontSpec != newFontSpec) {
4331                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4332             }
4333             break;
4334         }
4336         case 1:
4337         {
4338             if (!fontSpec.empty()) {
4339                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4340             }
4341             if (fontSpec != newFontSpec) {
4342                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4343             }
4344             break;
4345         }
4346     }
4348     if (!fontSpec.empty()) {
4349         sp_repr_css_set_property (css, "-inkscape-font-specification", fontSpec.c_str()); 
4350     }
4352     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4353     if (result_fontspec == QUERY_STYLE_NOTHING)
4354     {
4355         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4356     }
4358     sp_style_unref(query);
4360     sp_desktop_set_style (desktop, css, true, true);
4361     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4362                                    _("Text: Change font style"));
4363     sp_repr_css_attr_unref (css);
4365     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4368 void
4369 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4370                                      gpointer         data)
4372     if (g_object_get_data (G_OBJECT (button), "block")) {
4373         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4374         return;
4375     }
4377     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4378     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4379     int          prop       = GPOINTER_TO_INT(data);
4381     switch (prop)
4382     {
4383         case 0:
4384         {
4385             sp_repr_css_set_property (css, "writing-mode", "lr");
4386             break;
4387         }
4389         case 1:
4390         {
4391             sp_repr_css_set_property (css, "writing-mode", "tb");
4392             break;
4393         }
4394     }
4396     SPStyle *query =
4397         sp_style_new (SP_ACTIVE_DOCUMENT);
4398     int result_numbers =
4399         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4401     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4402     if (result_numbers == QUERY_STYLE_NOTHING)
4403     {
4404         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4405     }
4407     sp_desktop_set_style (desktop, css, true, true);
4408     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4409                                    _("Text: Change orientation"));
4410     sp_repr_css_attr_unref (css);
4412     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4415 gboolean
4416 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, gpointer /*data*/)
4418     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4419     if (!desktop) return FALSE;
4421     switch (get_group0_keyval (event)) {
4422         case GDK_Escape: // defocus
4423             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4424             return TRUE; // I consumed the event
4425             break;
4426         case GDK_Return: // defocus
4427         case GDK_KP_Enter:
4428             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4429             return TRUE; // I consumed the event
4430             break;
4431     }
4432     return FALSE;
4435 gboolean
4436 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
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             sp_text_toolbox_selection_changed (NULL, tbl); // update
4445             return TRUE; // I consumed the event
4446             break;
4447     }
4448     return FALSE;
4451 gboolean
4452 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4454     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4455     if (!desktop) return FALSE;
4457     switch (get_group0_keyval (event)) {
4458         case GDK_KP_Enter:
4459         case GDK_Return:
4460         case GDK_Escape: // defocus
4461             gtk_widget_hide (w);
4462             visible = false;
4463             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4464             return TRUE; // I consumed the event
4465             break;
4466     }
4467     return FALSE;
4471 void
4472 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4473                                GObject     *tbl)
4475     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4477     if (g_object_get_data (tbl, "size-block")) return;
4479     char *text = gtk_combo_box_get_active_text (cbox);
4481     SPCSSAttr *css = sp_repr_css_attr_new ();
4482     sp_repr_css_set_property (css, "font-size", text);
4483     free (text);
4485     SPStyle *query =
4486         sp_style_new (SP_ACTIVE_DOCUMENT);
4487     int result_numbers =
4488         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4490     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4491     if (result_numbers == QUERY_STYLE_NOTHING)
4492     {
4493         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4494     }
4496     sp_style_unref(query);
4498     sp_desktop_set_style (desktop, css, true, true);
4499     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4500                                    _("Text: Change font size"));
4501     sp_repr_css_attr_unref (css);
4504     if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
4505         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4508 void
4509 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4510                                          GObject            *tbl)
4512     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4513     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4514     int x, y;
4516     if (!visible)
4517     {
4518         gdk_window_get_origin (widget->window, &x, &y);
4519         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4520         gtk_widget_show_all (popdown);
4522         gdk_pointer_grab (widget->window, TRUE,
4523                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4524                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4525                                         GDK_POINTER_MOTION_MASK),
4526                           NULL, NULL, GDK_CURRENT_TIME);
4528         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4530         visible = true;
4531     }
4532     else
4533     {
4534         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4535         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4536         gtk_widget_hide (popdown);
4537         visible = false;
4538     }
4541 gboolean
4542 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4543                                  GdkEventFocus    */*event*/,
4544                                  GObject          */*tbl*/)
4546     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4547     return FALSE;
4550 gboolean
4551 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4552                                    GdkEventFocus    */*event*/,
4553                                    GObject          */*tbl*/)
4555     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4557     gtk_widget_hide (popdown);
4558     visible = false;
4559     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4560     return TRUE;
4563 void
4564 cell_data_func  (GtkTreeViewColumn */*column*/,
4565                  GtkCellRenderer   *cell,
4566                  GtkTreeModel      *tree_model,
4567                  GtkTreeIter       *iter,
4568                  gpointer           /*data*/)
4570     char        *family,
4571         *family_escaped,
4572         *sample_escaped;
4574     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4576     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4578     family_escaped = g_markup_escape_text (family, -1);
4579     sample_escaped = g_markup_escape_text (sample, -1);
4581     std::stringstream markup;
4582     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4583     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4585     free (family);
4586     free (family_escaped);
4587     free (sample_escaped);
4590 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4591     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4592     if (completion) {
4593         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4594         g_object_unref (completion);
4595     }
4598 GtkWidget*
4599 sp_text_toolbox_new (SPDesktop *desktop)
4601     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4603     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4604     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4606     GtkTooltips *tt = gtk_tooltips_new();
4607     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4609     ////////////Family
4610     //Window
4611     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4612     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4614     //Entry
4615     GtkWidget           *entry = gtk_entry_new ();
4616     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4617     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4618     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4619     gtk_entry_completion_set_text_column (completion, 0);
4620     gtk_entry_completion_set_minimum_key_length (completion, 1);
4621     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4622     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4623     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4624     aux_toolbox_space (tbl, 1);
4625     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4626     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4628     //Button
4629     GtkWidget   *button = gtk_button_new ();
4630     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4631     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4633     //Popdown
4634     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4635     GtkWidget           *treeview = gtk_tree_view_new ();
4637     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4638     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4639     gtk_tree_view_column_pack_start (column, cell, FALSE);
4640     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4641     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4642     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4644     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4645     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4646     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4648     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4650     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4651     gtk_container_add (GTK_CONTAINER (sw), treeview);
4653     gtk_container_add (GTK_CONTAINER (window), sw);
4654     gtk_widget_set_size_request (window, 300, 450);
4656     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4657     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4658     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4660     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4662     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4663     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4665     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4666     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4668     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4669     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4670     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4671     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4672     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4674     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4675     aux_toolbox_space (tbl, 1);
4676     GtkWidget *box = gtk_event_box_new ();
4677     gtk_container_add (GTK_CONTAINER (box), image);
4678     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4679     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4680     GtkTooltips *tooltips = gtk_tooltips_new ();
4681     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4682     gtk_widget_hide (GTK_WIDGET (box));
4683     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4685     ////////////Size
4686     const char *sizes[] = {
4687         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4688         "16", "18", "20", "22", "24", "28",
4689         "32", "36", "40", "48", "56", "64", "72", "144"
4690     };
4692     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4693     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4694     gtk_widget_set_size_request (cbox, 80, -1);
4695     aux_toolbox_space (tbl, 1);
4696     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4697     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4698     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4699     gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
4701     //spacer
4702     aux_toolbox_space (tbl, 4);
4703     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4705     ////////////Text anchor
4706     GtkWidget *group   = gtk_radio_button_new (NULL);
4707     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4708     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4710     // left
4711     GtkWidget *rbutton = group;
4712     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4713     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4714     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4716     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4717     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4718     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4719     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4721     // center
4722     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4723     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4724     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4725     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4727     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4728     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
4729     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4730     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4732     // right
4733     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4734     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4735     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4736     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4738     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4739     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
4740     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4741     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4743     // fill
4744     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4745     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4746     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4747     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4749     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4750     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
4751     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4752     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4754     aux_toolbox_space (tbl, 1);
4755     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4757     //spacer
4758     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4760     ////////////Text style
4761     row = gtk_hbox_new (FALSE, 4);
4763     // bold
4764     rbutton = gtk_toggle_button_new ();
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_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4767     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4768     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4770     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4771     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
4772     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4774     // italic
4775     rbutton = gtk_toggle_button_new ();
4776     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4777     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4778     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4779     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4781     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4782     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
4783     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4785     aux_toolbox_space (tbl, 1);
4786     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4788     //spacer
4789     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4791     ////////////Text orientation
4792     group   = gtk_radio_button_new (NULL);
4793     row     = gtk_hbox_new (FALSE, 4);
4794     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4796     // horizontal
4797     rbutton = group;
4798     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4799     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4800     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4801     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4803     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4804     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4805     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4807     // vertical
4808     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4809     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4810     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4811     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4812     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4814     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4815     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4816     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4817     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4820     //watch selection
4821     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4823     sigc::connection *c_selection_changed =
4824         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4825                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4826     pool->add_connection ("selection-changed", c_selection_changed);
4828     sigc::connection *c_selection_modified =
4829         new sigc::connection (sp_desktop_selection (desktop)->connectModified
4830                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4831     pool->add_connection ("selection-modified", c_selection_modified);
4833     sigc::connection *c_subselection_changed =
4834         new sigc::connection (desktop->connectToolSubselectionChanged
4835                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4836     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4838     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4841     gtk_widget_show_all (tbl);
4842     return tbl;
4844 } // end of sp_text_toolbox_new()
4846 }//<unnamed> namespace
4849 //#########################
4850 //##      Connector      ##
4851 //#########################
4853 static void sp_connector_path_set_avoid(void)
4855     cc_selection_set_avoid(true);
4859 static void sp_connector_path_set_ignore(void)
4861     cc_selection_set_avoid(false);
4866 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4868     // quit if run by the _changed callbacks
4869     if (g_object_get_data( tbl, "freeze" )) {
4870         return;
4871     }
4873     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4874     SPDocument *doc = sp_desktop_document(desktop);
4876     if (!sp_document_get_undo_sensitive(doc))
4877     {
4878         return;
4879     }
4881     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4883     if ( repr->attribute("inkscape:connector-spacing") ) {
4884         gdouble priorValue = gtk_adjustment_get_value(adj);
4885         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4886         if ( priorValue == gtk_adjustment_get_value(adj) ) {
4887             return;
4888         }
4889     } else if ( adj->value == defaultConnSpacing ) {
4890         return;
4891     }
4893     // in turn, prevent callbacks from responding
4894     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4896     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4897     SP_OBJECT(desktop->namedview)->updateRepr();
4899     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4900     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4901         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4902         NR::Matrix m = NR::identity();
4903         avoid_item_move(&m, item);
4904     }
4906     if (items) {
4907         g_slist_free(items);
4908     }
4910     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4911             _("Change connector spacing"));
4913     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4915     spinbutton_defocus(GTK_OBJECT(tbl));
4918 static void sp_connector_graph_layout(void)
4920     if (!SP_ACTIVE_DESKTOP) return;
4922     // hack for clones, see comment in align-and-distribute.cpp
4923     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4924     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4926     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
4928     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
4930     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
4933 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4935     if ( gtk_toggle_action_get_active( act ) ) {
4936         prefs_set_string_attribute("tools.connector", "directedlayout",
4937                 "true");
4938     } else {
4939         prefs_set_string_attribute("tools.connector", "directedlayout",
4940                 "false");
4941     }
4944 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4946     if ( gtk_toggle_action_get_active( act ) ) {
4947         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4948                 "true");
4949     } else {
4950         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4951                 "false");
4952     }
4956 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
4958     prefs_set_double_attribute("tools.connector", "length", adj->value);
4959     spinbutton_defocus(GTK_OBJECT(tbl));
4962 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
4963                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
4964                                             bool /*is_interactive*/, gpointer data)
4966     GtkWidget *tbl = GTK_WIDGET(data);
4968     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
4969         return;
4970     }
4971     if (strcmp(name, "inkscape:connector-spacing") != 0) {
4972         return;
4973     }
4975     GtkAdjustment *adj = (GtkAdjustment*)
4976             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
4977     gdouble spacing = defaultConnSpacing;
4978     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
4980     gtk_adjustment_set_value(adj, spacing);
4984 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
4985     NULL, /* child_added */
4986     NULL, /* child_removed */
4987     connector_tb_event_attr_changed,
4988     NULL, /* content_changed */
4989     NULL  /* order_changed */
4990 };
4993 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
4995     {
4996         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
4997                                           _("Avoid"),
4998                                           _("Make connectors avoid selected objects"),
4999                                           "connector_avoid",
5000                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5001         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5002         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5003     }
5005     {
5006         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5007                                           _("Ignore"),
5008                                           _("Make connectors ignore selected objects"),
5009                                           "connector_ignore",
5010                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5011         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5012         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5013     }
5015     EgeAdjustmentAction* eact = 0;
5017     // Spacing spinbox
5018     eact = create_adjustment_action( "ConnectorSpacingAction",
5019                                      _("Connector Spacing"), _("Spacing:"),
5020                                      _("The amount of space left around objects by auto-routing connectors"),
5021                                      "tools.connector", "spacing", defaultConnSpacing,
5022                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5023                                      0, 100, 1.0, 10.0,
5024                                      0, 0, 0,
5025                                      connector_spacing_changed, 1, 0 );
5026     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5028     // Graph (connector network) layout
5029     {
5030         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5031                                           _("Graph"),
5032                                           _("Nicely arrange selected connector network"),
5033                                           "graph_layout",
5034                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5035         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5036         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5037     }
5039     // Default connector length spinbox
5040     eact = create_adjustment_action( "ConnectorLengthAction",
5041                                      _("Connector Length"), _("Length:"),
5042                                      _("Ideal length for connectors when layout is applied"),
5043                                      "tools.connector", "length", 100,
5044                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5045                                      10, 1000, 10.0, 100.0,
5046                                      0, 0, 0,
5047                                      connector_length_changed, 1, 0 );
5048     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5051     // Directed edges toggle button
5052     {
5053         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5054                                                       _("Downwards"),
5055                                                       _("Make connectors with end-markers (arrows) point downwards"),
5056                                                       "directed_graph",
5057                                                       Inkscape::ICON_SIZE_DECORATION );
5058         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5060         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5061         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5062                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5064         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5065     }
5067     // Avoid overlaps toggle button
5068     {
5069         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5070                                                       _("Remove overlaps"),
5071                                                       _("Do not allow overlapping shapes"),
5072                                                       "remove_overlaps",
5073                                                       Inkscape::ICON_SIZE_DECORATION );
5074         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5076         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5077         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5078                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5080         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5081     }
5083     // Code to watch for changes to the connector-spacing attribute in
5084     // the XML.
5085     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5086     g_assert(repr != NULL);
5088     purge_repr_listener( holder, holder );
5090     if (repr) {
5091         g_object_set_data( holder, "repr", repr );
5092         Inkscape::GC::anchor(repr);
5093         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5094         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5095     }
5096 } // end of sp_connector_toolbox_prep()
5099 //#########################
5100 //##     Paintbucket     ##
5101 //#########################
5103 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5105     gint channels = ege_select_one_action_get_active( act );
5106     flood_channels_set_channels( channels );
5109 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5111     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5114 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5116     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5119 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5121     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5122     SPUnit const *unit = tracker->getActiveUnit();
5124     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5126     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5129 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5131     // FIXME: make defaults settable via Inkscape Options
5132     struct KeyValue {
5133         char const *key;
5134         double value;
5135     } const key_values[] = {
5136         {"threshold", 15},
5137         {"offset", 0.0}
5138     };
5140     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5141         KeyValue const &kv = key_values[i];
5142         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5143         if ( adj ) {
5144             gtk_adjustment_set_value(adj, kv.value);
5145         }
5146     }
5148     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5149     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5150     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5151     ege_select_one_action_set_active( autogap_action, 0 );
5154 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5156     EgeAdjustmentAction* eact = 0;
5158     {
5159         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5161         GList* items = 0;
5162         gint count = 0;
5163         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5164         {
5165             GtkTreeIter iter;
5166             gtk_list_store_append( model, &iter );
5167             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5168             count++;
5169         }
5170         g_list_free( items );
5171         items = 0;
5172         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5173         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5174         ege_select_one_action_set_appearance( act1, "compact" );
5175         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5176         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5177         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5178         g_object_set_data( holder, "channels_action", act1 );
5179     }
5181     // Spacing spinbox
5182     {
5183         eact = create_adjustment_action(
5184             "ThresholdAction",
5185             _("Fill Threshold"), _("Threshold:"),
5186             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5187             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5188             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5189             0, 0, 0,
5190             paintbucket_threshold_changed, 1, 0 );
5192         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5193     }
5195     // Create the units menu.
5196     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5197     tracker->setActiveUnit(sp_unit_get_by_abbreviation(prefs_get_string_attribute("tools.paintbucket", "offsetunits")));
5198     g_object_set_data( holder, "tracker", tracker );
5199     {
5200         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5201         gtk_action_group_add_action( mainActions, act );
5202     }
5204     // Offset spinbox
5205     {
5206         eact = create_adjustment_action(
5207             "OffsetAction",
5208             _("Grow/shrink by"), _("Grow/shrink by:"),
5209             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5210             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5211             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5212             0, 0, 0,
5213             paintbucket_offset_changed, 1, 2);
5214         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5216         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5217     }
5219     /* Auto Gap */
5220     {
5221         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5223         GList* items = 0;
5224         gint count = 0;
5225         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5226         {
5227             GtkTreeIter iter;
5228             gtk_list_store_append( model, &iter );
5229             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5230             count++;
5231         }
5232         g_list_free( items );
5233         items = 0;
5234         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5235         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5236         ege_select_one_action_set_appearance( act2, "compact" );
5237         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5238         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5239         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5240         g_object_set_data( holder, "autogap_action", act2 );
5241     }
5243     /* Reset */
5244     {
5245         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5246                                           _("Defaults"),
5247                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5248                                           GTK_STOCK_CLEAR );
5249         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5250         gtk_action_group_add_action( mainActions, act );
5251         gtk_action_set_sensitive( act, TRUE );
5252     }
5256 /*
5257   Local Variables:
5258   mode:c++
5259   c-file-style:"stroustrup"
5260   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5261   indent-tabs-mode:nil
5262   fill-column:99
5263   End:
5264 */
5265 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :