Code

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