Code

Fundamentally reworked version of the 3D box tool (among many other things, this...
[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 "../connection-pool.h"
80 #include "../prefs-utils.h"
81 #include "../inkscape-stock.h"
82 #include "icon.h"
83 #include "graphlayout/graphlayout.h"
85 #include "mod360.h"
87 #include "toolbox.h"
89 #include "flood-context.h"
91 #include "ink-action.h"
92 #include "ege-adjustment-action.h"
93 #include "ege-output-action.h"
94 #include "ege-select-one-action.h"
95 #include "helper/unit-tracker.h"
97 using Inkscape::UnitTracker;
99 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
100 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
102 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
103 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
104 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
105 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
106 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
107 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
108 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
109 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
115 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
121 static struct {
122     gchar const *type_name;
123     gchar const *data_name;
124     sp_verb_t verb;
125     sp_verb_t doubleclick_verb;
126 } const tools[] = {
127     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
128     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
129     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
130     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
131     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
132     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
133     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
134     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
135     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
136     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
137     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
138     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
139     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
140     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
141     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
142     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
143     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
144     { NULL, NULL, 0, 0 }
145 };
147 static struct {
148     gchar const *type_name;
149     gchar const *data_name;
150     GtkWidget *(*create_func)(SPDesktop *desktop);
151     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
152     gchar const *ui_name;
153     gint swatch_verb_id;
154     gchar const *swatch_tool;
155     gchar const *swatch_tip;
156 } const aux_toolboxes[] = {
157     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
158       SP_VERB_INVALID, 0, 0},
159     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
160       SP_VERB_INVALID, 0, 0},
161     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
162       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
163     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
164       SP_VERB_INVALID, 0, 0},
165     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
166       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     _("Style of new stars")},
167     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
168       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     _("Style of new rectangles")},
169     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
170       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    _("Style of new 3D boxes")},
171     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
172       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      _("Style of new ellipses")},
173     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
174       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   _("Style of new spirals")},
175     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
176       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
177     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
178       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    _("Style of new paths created by Pen")},
179     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
180       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
181     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
182       SP_VERB_INVALID, 0, 0},
183     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
184       SP_VERB_INVALID, 0, 0},
185     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
186       SP_VERB_INVALID, 0, 0},
187     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
188       SP_VERB_INVALID, 0, 0},
189     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
190       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
191     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
192 };
195 static gchar const * ui_descr =
196         "<ui>"
197         "  <toolbar name='SelectToolbar'>"
198         "    <toolitem action='EditSelectAll' />"
199         "    <toolitem action='EditSelectAllInAllLayers' />"
200         "    <toolitem action='EditDeselect' />"
201         "    <separator />"
202         "    <toolitem action='ObjectRotate90CCW' />"
203         "    <toolitem action='ObjectRotate90' />"
204         "    <toolitem action='ObjectFlipHorizontally' />"
205         "    <toolitem action='ObjectFlipVertically' />"
206         "    <separator />"
207         "    <toolitem action='SelectionToBack' />"
208         "    <toolitem action='SelectionLower' />"
209         "    <toolitem action='SelectionRaise' />"
210         "    <toolitem action='SelectionToFront' />"
211         "    <separator />"
212         "    <toolitem action='XAction' />"
213         "    <toolitem action='YAction' />"
214         "    <toolitem action='WidthAction' />"
215         "    <toolitem action='LockAction' />"
216         "    <toolitem action='HeightAction' />"
217         "    <toolitem action='UnitsAction' />"
218         "    <separator />"
219         "    <toolitem action='transform_affect_label' />"
220         "    <toolitem action='transform_stroke' />"
221         "    <toolitem action='transform_corners' />"
222         "    <toolitem action='transform_gradient' />"
223         "    <toolitem action='transform_pattern' />"
224         "  </toolbar>"
226         "  <toolbar name='NodeToolbar'>"
227         "    <toolitem action='NodeInsertAction' />"
228         "    <toolitem action='NodeDeleteAction' />"
229         "    <separator />"
230         "    <toolitem action='NodeJoinAction' />"
231         "    <toolitem action='NodeJoinSegmentAction' />"
232         "    <toolitem action='NodeDeleteSegmentAction' />"
233         "    <toolitem action='NodeBreakAction' />"
234         "    <separator />"
235         "    <toolitem action='NodeCuspAction' />"
236         "    <toolitem action='NodeSmoothAction' />"
237         "    <toolitem action='NodeSymmetricAction' />"
238         "    <separator />"
239         "    <toolitem action='NodeLineAction' />"
240         "    <toolitem action='NodeCurveAction' />"
241         "    <separator />"
242         "    <toolitem action='ObjectToPath' />"
243         "    <toolitem action='StrokeToPath' />"
244         "    <separator />"
245         "    <toolitem action='NodesShowHandlesAction' />"
246         "    <separator />"
247         "    <toolitem action='EditNextLPEParameterAction' />"
248         "    <separator />"
249         "    <toolitem action='NodeXAction' />"
250         "    <toolitem action='NodeYAction' />"
251         "    <toolitem action='NodeUnitsAction' />"
252         "  </toolbar>"
254         "  <toolbar name='TweakToolbar'>"
255         "    <toolitem action='TweakWidthAction' />"
256         "    <separator />"
257         "    <toolitem action='TweakForceAction' />"
258         "    <toolitem action='TweakPressureAction' />"
259         "    <separator />"
260         "    <toolitem action='TweakModeAction' />"
261         "    <separator />"
262         "    <toolitem action='TweakFidelityAction' />"
263         "    <separator />"
264         "    <toolitem action='TweakChannelsLabel' />"
265         "    <toolitem action='TweakDoH' />"
266         "    <toolitem action='TweakDoS' />"
267         "    <toolitem action='TweakDoL' />"
268         "    <toolitem action='TweakDoO' />"
269         "  </toolbar>"
271         "  <toolbar name='ZoomToolbar'>"
272         "    <toolitem action='ZoomIn' />"
273         "    <toolitem action='ZoomOut' />"
274         "    <separator />"
275         "    <toolitem action='Zoom1:0' />"
276         "    <toolitem action='Zoom1:2' />"
277         "    <toolitem action='Zoom2:1' />"
278         "    <separator />"
279         "    <toolitem action='ZoomSelection' />"
280         "    <toolitem action='ZoomDrawing' />"
281         "    <toolitem action='ZoomPage' />"
282         "    <toolitem action='ZoomPageWidth' />"
283         "    <separator />"
284         "    <toolitem action='ZoomPrev' />"
285         "    <toolitem action='ZoomNext' />"
286         "  </toolbar>"
288         "  <toolbar name='StarToolbar'>"
289         "    <separator />"
290         "    <toolitem action='StarStateAction' />"
291         "    <separator />"
292         "    <toolitem action='FlatAction' />"
293         "    <separator />"
294         "    <toolitem action='MagnitudeAction' />"
295         "    <toolitem action='SpokeAction' />"
296         "    <toolitem action='RoundednessAction' />"
297         "    <toolitem action='RandomizationAction' />"
298         "    <separator />"
299         "    <toolitem action='StarResetAction' />"
300         "  </toolbar>"
302         "  <toolbar name='RectToolbar'>"
303         "    <toolitem action='RectStateAction' />"
304         "    <toolitem action='RectWidthAction' />"
305         "    <toolitem action='RectHeightAction' />"
306         "    <toolitem action='RadiusXAction' />"
307         "    <toolitem action='RadiusYAction' />"
308         "    <toolitem action='RectUnitsAction' />"
309         "    <separator />"
310         "    <toolitem action='RectResetAction' />"
311         "  </toolbar>"
313         "  <toolbar name='3DBoxToolbar'>"
314         "    <toolitem action='3DBoxPosAngleXAction' />"
315         "    <toolitem action='3DBoxVPXAction' />"
316         "    <separator />"
317         "    <toolitem action='3DBoxPosAngleYAction' />"
318         "    <toolitem action='3DBoxVPYAction' />"
319         "    <separator />"
320         "    <toolitem action='3DBoxPosAngleZAction' />"
321         "    <toolitem action='3DBoxVPZAction' />"
322         "    <separator />"
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* calligraphy_angle = 0;
1877         EgeAdjustmentAction* eact = 0;
1878         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1879         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1881         /* Flatsided checkbox */
1882         {
1883             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1885             GtkTreeIter iter;
1886             gtk_list_store_append( model, &iter );
1887             gtk_list_store_set( model, &iter,
1888                                 0, _("Polygon"),
1889                                 1, _("Regular polygon (with one handle) instead of a star"),
1890                                 2, "star_flat",
1891                                 -1 );
1893             gtk_list_store_append( model, &iter );
1894             gtk_list_store_set( model, &iter,
1895                                 0, _("Star"),
1896                                 1, _("Star instead of a regular polygon (with one handle)"),
1897                                 2, "star_angled",
1898                                 -1 );
1900             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
1901             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1902             g_object_set_data( holder, "flat_action", act );
1904             ege_select_one_action_set_appearance( act, "full" );
1905             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1906             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1907             ege_select_one_action_set_icon_column( act, 2 );
1908             ege_select_one_action_set_tooltip_column( act, 1  );
1910             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1911             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1912         }
1914         /* Magnitude */
1915         {
1916         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1917         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1918         eact = create_adjustment_action( "MagnitudeAction",
1919                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
1920                                          "tools.shapes.star", "magnitude", 3,
1921                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1922                                          3, 1024, 1, 5,
1923                                          labels, values, G_N_ELEMENTS(labels),
1924                                          sp_stb_magnitude_value_changed,
1925                                          1.0, 0 );
1926         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1927         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1928         }
1930         /* Spoke ratio */
1931         {
1932         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1933         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1934         eact = create_adjustment_action( "SpokeAction",
1935                                          _("Spoke ratio"), _("Spoke ratio:"),
1936                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1937                                          // Base radius is the same for the closest handle.
1938                                          _("Base radius to tip radius ratio"),
1939                                          "tools.shapes.star", "proportion", 0.5,
1940                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1941                                          0.01, 1.0, 0.01, 0.1,
1942                                          labels, values, G_N_ELEMENTS(labels),
1943                                          sp_stb_proportion_value_changed );
1944         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1945         g_object_set_data( holder, "prop_action", eact );
1946         }
1948         if ( !isFlatSided ) {
1949             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1950         } else {
1951             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1952         }
1954         /* Roundedness */
1955         {
1956         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1957         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1958         eact = create_adjustment_action( "RoundednessAction",
1959                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1960                                          "tools.shapes.star", "rounded", 0.0,
1961                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1962                                          -10.0, 10.0, 0.01, 0.1,
1963                                          labels, values, G_N_ELEMENTS(labels),
1964                                          sp_stb_rounded_value_changed );
1965         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1966         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1967         }
1969         /* Randomization */
1970         {
1971         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1972         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1973         eact = create_adjustment_action( "RandomizationAction",
1974                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
1975                                          "tools.shapes.star", "randomized", 0.0,
1976                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1977                                          -10.0, 10.0, 0.001, 0.01,
1978                                          labels, values, G_N_ELEMENTS(labels),
1979                                          sp_stb_randomized_value_changed, 0.1, 3 );
1980         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1981         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1982         }
1983     }
1985     {
1986         /* Reset */
1987         {
1988             GtkAction* act = gtk_action_new( "StarResetAction",
1989                                              _("Defaults"),
1990                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1991                                              GTK_STOCK_CLEAR );
1992             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1993             gtk_action_group_add_action( mainActions, act );
1994             gtk_action_set_sensitive( act, TRUE );
1995         }
1996     }
1998     sigc::connection *connection = new sigc::connection(
1999         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2000         );
2001     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2002     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2006 //########################
2007 //##       Rect         ##
2008 //########################
2010 static void sp_rtb_sensitivize( GObject *tbl )
2012     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2013     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2014     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2016     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2017         gtk_action_set_sensitive( not_rounded, FALSE );
2018     } else {
2019         gtk_action_set_sensitive( not_rounded, TRUE );
2020     }
2024 static void
2025 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2026                           void (*setter)(SPRect *, gdouble))
2028     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2030     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2031     SPUnit const *unit = tracker->getActiveUnit();
2033     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2034         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2035     }
2037     // quit if run by the attr_changed listener
2038     if (g_object_get_data( tbl, "freeze" )) {
2039         return;
2040     }
2042     // in turn, prevent listener from responding
2043     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2045     bool modmade = false;
2046     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2047     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2048         if (SP_IS_RECT(items->data)) {
2049             if (adj->value != 0) {
2050                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2051             } else {
2052                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2053             }
2054             modmade = true;
2055         }
2056     }
2058     sp_rtb_sensitivize( tbl );
2060     if (modmade) {
2061         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2062                                    _("Change rectangle"));
2063     }
2065     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2068 static void
2069 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2071     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2074 static void
2075 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2077     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2080 static void
2081 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2083     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2086 static void
2087 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2089     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2094 static void
2095 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2097     GtkAdjustment *adj = 0;
2099     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2100     gtk_adjustment_set_value(adj, 0.0);
2101     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2102     gtk_adjustment_value_changed(adj);
2104     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2105     gtk_adjustment_set_value(adj, 0.0);
2106     gtk_adjustment_value_changed(adj);
2108     sp_rtb_sensitivize( obj );
2111 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2112                                        gchar const */*old_value*/, gchar const */*new_value*/,
2113                                        bool /*is_interactive*/, gpointer data)
2115     GObject *tbl = G_OBJECT(data);
2117     // quit if run by the _changed callbacks
2118     if (g_object_get_data( tbl, "freeze" )) {
2119         return;
2120     }
2122     // in turn, prevent callbacks from responding
2123     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2125     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2126     SPUnit const *unit = tracker->getActiveUnit();
2128     gpointer item = g_object_get_data( tbl, "item" );
2129     if (item && SP_IS_RECT(item)) {
2130         {
2131             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2132             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2133             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2134         }
2136         {
2137             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2138             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2139             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2140         }
2142         {
2143             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2144             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2145             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2146         }
2148         {
2149             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2150             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2151             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2152         }
2153     }
2155     sp_rtb_sensitivize( tbl );
2157     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2161 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2162     NULL, /* child_added */
2163     NULL, /* child_removed */
2164     rect_tb_event_attr_changed,
2165     NULL, /* content_changed */
2166     NULL  /* order_changed */
2167 };
2169 /**
2170  *  \param selection should not be NULL.
2171  */
2172 static void
2173 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2175     int n_selected = 0;
2176     Inkscape::XML::Node *repr = NULL;
2177     SPItem *item = NULL;
2179     if ( g_object_get_data( tbl, "repr" ) ) {
2180         g_object_set_data( tbl, "item", NULL );
2181     }
2182     purge_repr_listener( tbl, tbl );
2184     for (GSList const *items = selection->itemList();
2185          items != NULL;
2186          items = items->next) {
2187         if (SP_IS_RECT((SPItem *) items->data)) {
2188             n_selected++;
2189             item = (SPItem *) items->data;
2190             repr = SP_OBJECT_REPR(item);
2191         }
2192     }
2194     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2196     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2198     if (n_selected == 0) {
2199         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2201         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2202         gtk_action_set_sensitive(w, FALSE);
2203         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2204         gtk_action_set_sensitive(h, FALSE);
2206     } else if (n_selected == 1) {
2207         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2208         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2210         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2211         gtk_action_set_sensitive(w, TRUE);
2212         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2213         gtk_action_set_sensitive(h, TRUE);
2215         if (repr) {
2216             g_object_set_data( tbl, "repr", repr );
2217             g_object_set_data( tbl, "item", item );
2218             Inkscape::GC::anchor(repr);
2219             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2220             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2221         }
2222     } else {
2223         // FIXME: implement averaging of all parameters for multiple selected
2224         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2225         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2226         sp_rtb_sensitivize( tbl );
2227     }
2231 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2233     EgeAdjustmentAction* eact = 0;
2235     {
2236         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2237         ege_output_action_set_use_markup( act, TRUE );
2238         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2239         g_object_set_data( holder, "mode_action", act );
2240     }
2242     // rx/ry units menu: create
2243     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2244     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2245     // fixme: add % meaning per cent of the width/height
2246     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2247     g_object_set_data( holder, "tracker", tracker );
2249     /* W */
2250     {
2251         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2252         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2253         eact = create_adjustment_action( "RectWidthAction",
2254                                          _("Width"), _("W:"), _("Width of rectangle"),
2255                                          "tools.shapes.rect", "width", 0,
2256                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2257                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2258                                          labels, values, G_N_ELEMENTS(labels),
2259                                          sp_rtb_width_value_changed );
2260         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2261         g_object_set_data( holder, "width_action", eact );
2262         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2263         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2264     }
2266     /* H */
2267     {
2268         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2269         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2270         eact = create_adjustment_action( "RectHeightAction",
2271                                          _("Height"), _("H:"), _("Height of rectangle"),
2272                                          "tools.shapes.rect", "height", 0,
2273                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2274                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2275                                          labels, values, G_N_ELEMENTS(labels),
2276                                          sp_rtb_height_value_changed );
2277         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2278         g_object_set_data( holder, "height_action", eact );
2279         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2280         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2281     }
2283     /* rx */
2284     {
2285         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2286         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2287         eact = create_adjustment_action( "RadiusXAction",
2288                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2289                                          "tools.shapes.rect", "rx", 0,
2290                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2291                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2292                                          labels, values, G_N_ELEMENTS(labels),
2293                                          sp_rtb_rx_value_changed);
2294         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2295         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2296     }
2298     /* ry */
2299     {
2300         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2301         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2302         eact = create_adjustment_action( "RadiusYAction",
2303                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2304                                          "tools.shapes.rect", "ry", 0,
2305                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2306                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2307                                          labels, values, G_N_ELEMENTS(labels),
2308                                          sp_rtb_ry_value_changed);
2309         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2310         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2311     }
2313     // add the units menu
2314     {
2315         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2316         gtk_action_group_add_action( mainActions, act );
2317     }
2319     /* Reset */
2320     {
2321         InkAction* inky = ink_action_new( "RectResetAction",
2322                                           _("Not rounded"),
2323                                           _("Make corners sharp"),
2324                                           "squared_corner",
2325                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2326         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2327         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2328         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2329         g_object_set_data( holder, "not_rounded", inky );
2330     }
2332     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2333     sp_rtb_sensitivize( holder );
2335     sigc::connection *connection = new sigc::connection(
2336         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2337         );
2338     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2339     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2342 //########################
2343 //##       3D Box       ##
2344 //########################
2346 static void box3d_toggle_vp_changed (GtkToggleAction *act, GObject *dataKludge, Proj::Axis axis)
2348     SPDesktop *desktop = (SPDesktop *) g_object_get_data (dataKludge, "desktop");
2349     SPDocument *document = sp_desktop_document (desktop);
2350     // FIXME: Make sure document->current_persp3d is set correctly!
2351     Persp3D *persp = document->current_persp3d;
2353     g_return_if_fail (persp);
2355     // quit if run by the attr_changed listener
2356     if (g_object_get_data(dataKludge, "freeze")) {
2357         return;
2358     }
2360     // in turn, prevent listener from responding
2361     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
2363     persp3d_set_VP_state(persp, axis, gtk_toggle_action_get_active(act) ? Proj::INFINITE : Proj::FINITE);
2365     // FIXME: Can we merge this functionality with the one in box3d_persp_tb_event_attr_changed()?
2366     gchar *str;    
2367     switch (axis) {
2368         case Proj::X:
2369             str = g_strdup ("box3d_angle_x_action");
2370             break;
2371         case Proj::Y:
2372             str = g_strdup ("box3d_angle_y_action");
2373             break;
2374         case Proj::Z:
2375             str = g_strdup ("box3d_angle_z_action");
2376             break;
2377         default:
2378             return;
2379     }
2380     GtkAction* angle_action = GTK_ACTION (g_object_get_data (dataKludge, str));
2381     if (angle_action) {
2382         gtk_action_set_sensitive (angle_action, !persp3d_VP_is_finite(persp, axis));
2383     }
2385     sp_document_maybe_done(sp_desktop_document(desktop), "toggle_vp", SP_VERB_CONTEXT_3DBOX,
2386                            _("3D Box: Toggle VP"));
2387     //sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,_("3D Box: Toggle VP"));
2389     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(FALSE));
2392 static void box3d_toggle_vp_x_changed(GtkToggleAction *act, GObject *dataKludge)
2394     box3d_toggle_vp_changed (act, dataKludge, Proj::X);
2397 static void box3d_toggle_vp_y_changed(GtkToggleAction *act, GObject *dataKludge)
2399     box3d_toggle_vp_changed (act, dataKludge, Proj::Y);
2402 static void box3d_toggle_vp_z_changed(GtkToggleAction *act, GObject *dataKludge)
2404     box3d_toggle_vp_changed (act, dataKludge, Proj::Z);
2407 static void box3d_vp_angle_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis )
2409     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
2410     Persp3D *persp = sp_desktop_document (desktop)->current_persp3d;
2412     // quit if run by the attr_changed listener
2413     if (g_object_get_data(dataKludge, "freeze")) {
2414         return;
2415     }
2417     // in turn, prevent listener from responding
2418     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
2420     if (persp) {
2421         double angle = adj->value;
2422         // FIXME: Shouldn't we set the angle via the SVG attributes of the perspective instead of directly?
2423         if (persp3d_VP_is_finite(persp, axis)) {
2424             return; 
2425         }
2426         persp->tmat.set_infinite_direction (axis, angle);
2427         persp3d_update_box_reprs (persp);
2429         sp_document_maybe_done(sp_desktop_document(desktop), "perspectiveangle", SP_VERB_CONTEXT_3DBOX,
2430                                _("3D Box: Change perspective"));
2431     }
2433     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(FALSE));
2436 static void box3d_vpx_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2438     box3d_vp_angle_changed (adj, dataKludge, Proj::X);
2441 static void box3d_vpy_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2443     box3d_vp_angle_changed (adj, dataKludge, Proj::Y);
2446 static void box3d_vpz_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2448     box3d_vp_angle_changed (adj, dataKludge, Proj::Z);
2451 // normalize angle so that it lies in the interval [0,360]
2452 static double box3d_normalize_angle (double a) {
2453     double angle = a + ((int) (a/360.0))*360;
2454     if (angle < 0) {
2455         angle += 360.0;
2456     }
2457     return angle;
2460 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2461                                               gchar const *old_value, gchar const *new_value,
2462                                               bool is_interactive, gpointer data)
2464     GtkWidget *tbl = GTK_WIDGET(data);
2466     // FIXME: if we check for "freeze" as in other tools, no action is performed at all ...
2467     // quit if run by the _changed callbacks
2468     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2469         //return;
2470     }
2472     // in turn, prevent callbacks from responding
2473     //g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2475     GtkAdjustment *adj = 0;
2476     double angle;
2477     SPDesktop *desktop = (SPDesktop *) g_object_get_data(G_OBJECT(tbl), "desktop");
2478     // FIXME: Get the persp from the box (should be the same, but ...)
2479     Persp3D *persp = sp_desktop_document (desktop)->current_persp3d;
2480     if (!strcmp(name, "inkscape:vp_x")) {
2481         GtkAction* act = GTK_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "box3d_angle_x_action"));
2482         GtkToggleAction* tact = GTK_TOGGLE_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "toggle_vp_x_action"));
2483         if (!persp3d_VP_is_finite(persp, Proj::X)) {
2484             gtk_action_set_sensitive(GTK_ACTION(act), TRUE);
2485             gtk_toggle_action_set_active(tact, TRUE);
2486         } else {
2487             gtk_action_set_sensitive(GTK_ACTION(act), FALSE);
2488             gtk_toggle_action_set_active(tact, FALSE);
2489         }
2491         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_x"));
2492         angle = persp3d_get_infinite_angle(persp, Proj::X);
2493         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2494             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2495         }
2496     }
2498     if (!strcmp(name, "inkscape:vp_y")) {
2499         GtkAction* act = GTK_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "box3d_angle_y_action"));
2500         GtkToggleAction* tact = GTK_TOGGLE_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "toggle_vp_y_action"));
2501         if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2502             gtk_action_set_sensitive(GTK_ACTION(act), TRUE);
2503             gtk_toggle_action_set_active(tact, TRUE);
2504         } else {
2505             gtk_action_set_sensitive(GTK_ACTION(act), FALSE);
2506             gtk_toggle_action_set_active(tact, FALSE);
2507         }
2509         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_y"));
2510         angle = persp3d_get_infinite_angle(persp, Proj::Y);
2511         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2512             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2513         }
2514     }
2516     if (!strcmp(name, "inkscape:vp_z")) {
2517         GtkAction* act = GTK_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "box3d_angle_z_action"));
2518         GtkToggleAction* tact = GTK_TOGGLE_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "toggle_vp_z_action"));
2519         if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2520             gtk_action_set_sensitive(GTK_ACTION(act), TRUE);
2521             gtk_toggle_action_set_active(tact, TRUE);
2522         } else {
2523             gtk_action_set_sensitive(GTK_ACTION(act), FALSE);
2524             gtk_toggle_action_set_active(tact, FALSE);
2525         }
2527         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_z"));
2528         angle = persp3d_get_infinite_angle(persp, Proj::Z);
2529         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2530             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2531         }
2532     }
2533     
2534     //g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2537 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2539     NULL, /* child_added */
2540     NULL, /* child_removed */
2541     box3d_persp_tb_event_attr_changed,
2542     NULL, /* content_changed */
2543     NULL  /* order_changed */
2544 };
2546 /**
2547  *  \param selection Should not be NULL.
2548  */
2549 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2550 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2551 static void
2552 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2554     Inkscape::XML::Node *repr = NULL;
2555     purge_repr_listener(tbl, tbl);
2557     SPItem *item = selection->singleItem();
2558     if (item && SP_IS_BOX3D(item)) {
2559         //repr = SP_OBJECT_REPR(item);
2560         repr = SP_OBJECT_REPR(SP_BOX3D(item)->persp_ref->getObject());
2561         if (repr) {
2562             g_object_set_data(tbl, "repr", repr);
2563             Inkscape::GC::anchor(repr);
2564             sp_repr_add_listener(repr, &box3d_persp_tb_repr_events, tbl);
2565             sp_repr_synthesize_events(repr, &box3d_persp_tb_repr_events, tbl);
2566         }
2567     }
2570 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2572     EgeAdjustmentAction* eact = 0;
2573     SPDocument *document = sp_desktop_document (desktop);
2574     Persp3D *persp = document->current_persp3d;
2575     bool toggled = false;
2577     /* angle of VP in X direction */
2578     eact = create_adjustment_action("3DBoxPosAngleXAction",
2579                                     _("Angle X"), _("Angle X:"), _("Angle of infinite vanishing point in X direction"),
2580                                     "tools.shapes.3dbox", "dir_vp_x", persp3d_get_infinite_angle(persp, Proj::X),
2581                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2582                                     -360.0, 360.0, 1.0, 10.0,
2583                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2584                                     box3d_vpx_angle_changed,
2585                                     0.1, 1);
2586     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2587     g_object_set_data(holder, "box3d_angle_x_action", eact);
2588     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2589         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2590     } else {
2591         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2592     }
2594     /* toggle VP in X direction */
2595     {
2596     InkToggleAction* act = ink_toggle_action_new("3DBoxVPXAction",
2597                                                   _("Toggle VP in X direction"),
2598                                                   _("Toggle VP in X direction between 'finite' and 'infinite' (= parallel)"),
2599                                                   "toggle_vp_x",
2600                                                   Inkscape::ICON_SIZE_DECORATION);
2601     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2602     g_object_set_data(holder, "toggle_vp_x_action", act);
2603     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), !persp3d_VP_is_finite(persp, Proj::X));
2604     /* we connect the signal after setting the state to avoid switching the state again */
2605     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(box3d_toggle_vp_x_changed), holder);
2606     }
2608     /* angle of VP in Y direction */
2609     eact = create_adjustment_action("3DBoxPosAngleYAction",
2610                                     _("Angle Y"), _("Angle Y:"), _("Angle of infinite vanishing point in Y direction"),
2611                                     "tools.shapes.3dbox", "dir_vp_y", persp3d_get_infinite_angle(persp, Proj::Y),
2612                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2613                                     -360.0, 360.0, 1.0, 10.0,
2614                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2615                                     box3d_vpy_angle_changed,
2616                                     0.1, 1);
2617     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2618     g_object_set_data(holder, "box3d_angle_y_action", eact);
2619     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2620         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2621     } else {
2622         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2623     }
2625     /* toggle VP in Y direction */
2626     {
2627     InkToggleAction* act = ink_toggle_action_new("3DBoxVPYAction",
2628                                                  _("Toggle VP in Y direction"),
2629                                                  _("Toggle VP in Y direction between 'finite' and 'infinite' (= parallel)"),
2630                                                  "toggle_vp_y",
2631                                                  Inkscape::ICON_SIZE_DECORATION);
2632     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2633     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), !persp3d_VP_is_finite(persp, Proj::Y));
2634     g_object_set_data(holder, "toggle_vp_y_action", act);
2635     /* we connect the signal after setting the state to avoid switching the state again */
2636     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(box3d_toggle_vp_y_changed), holder);
2637     }
2639     /* angle of VP in Z direction */
2640     eact = create_adjustment_action("3DBoxPosAngleZAction",
2641                                     _("Angle Z"), _("Angle Z:"), _("Angle of infinite vanishing point in Z direction"),
2642                                     "tools.shapes.3dbox", "dir_vp_z", persp3d_get_infinite_angle(persp, Proj::Z),
2643                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2644                                     -360.0, 360.0, 1.0, 10.0,
2645                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2646                                     box3d_vpz_angle_changed,
2647                                     0.1, 1);
2649     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2650     g_object_set_data(holder, "box3d_angle_z_action", eact);
2651     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2652         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2653     } else {
2654         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2655     }
2657     /* toggle VP in Z direction */
2658     {
2659     InkToggleAction* act = ink_toggle_action_new("3DBoxVPZAction",
2660                                                  _("Toggle VP in Z direction"),
2661                                                  _("Toggle VP in Z direction between 'finite' and 'infinite' (= parallel)"),
2662                                                  "toggle_vp_z",
2663                                                  Inkscape::ICON_SIZE_DECORATION);
2664     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2666     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), !persp3d_VP_is_finite(persp, Proj::Z));
2667     g_object_set_data(holder, "toggle_vp_z_action", act);
2668     /* we connect the signal after setting the state to avoid switching the state again */
2669     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(box3d_toggle_vp_z_changed), holder);
2670     }
2672     sigc::connection *connection = new sigc::connection(
2673         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2674        );
2675     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2676     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2679 //########################
2680 //##       Spiral       ##
2681 //########################
2683 static void
2684 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2686     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2688     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2689         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2690     }
2692     // quit if run by the attr_changed listener
2693     if (g_object_get_data( tbl, "freeze" )) {
2694         return;
2695     }
2697     // in turn, prevent listener from responding
2698     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2700     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2702     bool modmade = false;
2703     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2704          items != NULL;
2705          items = items->next)
2706     {
2707         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2708             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2709             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2710             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2711             modmade = true;
2712         }
2713     }
2715     g_free(namespaced_name);
2717     if (modmade) {
2718         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2719                                    _("Change spiral"));
2720     }
2722     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2725 static void
2726 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2728     sp_spl_tb_value_changed(adj, tbl, "revolution");
2731 static void
2732 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2734     sp_spl_tb_value_changed(adj, tbl, "expansion");
2737 static void
2738 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2740     sp_spl_tb_value_changed(adj, tbl, "t0");
2743 static void
2744 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2746     GtkWidget *tbl = GTK_WIDGET(obj);
2748     GtkAdjustment *adj;
2750     // fixme: make settable
2751     gdouble rev = 5;
2752     gdouble exp = 1.0;
2753     gdouble t0 = 0.0;
2755     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2756     gtk_adjustment_set_value(adj, rev);
2757     gtk_adjustment_value_changed(adj);
2759     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2760     gtk_adjustment_set_value(adj, exp);
2761     gtk_adjustment_value_changed(adj);
2763     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2764     gtk_adjustment_set_value(adj, t0);
2765     gtk_adjustment_value_changed(adj);
2767     spinbutton_defocus(GTK_OBJECT(tbl));
2771 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2772                                          gchar const */*old_value*/, gchar const */*new_value*/,
2773                                          bool /*is_interactive*/, gpointer data)
2775     GtkWidget *tbl = GTK_WIDGET(data);
2777     // quit if run by the _changed callbacks
2778     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2779         return;
2780     }
2782     // in turn, prevent callbacks from responding
2783     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2785     GtkAdjustment *adj;
2786     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2787     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2789     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2790     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2792     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2793     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2795     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2799 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2800     NULL, /* child_added */
2801     NULL, /* child_removed */
2802     spiral_tb_event_attr_changed,
2803     NULL, /* content_changed */
2804     NULL  /* order_changed */
2805 };
2807 static void
2808 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2810     int n_selected = 0;
2811     Inkscape::XML::Node *repr = NULL;
2813     purge_repr_listener( tbl, tbl );
2815     for (GSList const *items = selection->itemList();
2816          items != NULL;
2817          items = items->next)
2818     {
2819         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2820             n_selected++;
2821             repr = SP_OBJECT_REPR((SPItem *) items->data);
2822         }
2823     }
2825     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2827     if (n_selected == 0) {
2828         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2829     } else if (n_selected == 1) {
2830         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2832         if (repr) {
2833             g_object_set_data( tbl, "repr", repr );
2834             Inkscape::GC::anchor(repr);
2835             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2836             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2837         }
2838     } else {
2839         // FIXME: implement averaging of all parameters for multiple selected
2840         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2841         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2842     }
2846 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2848     EgeAdjustmentAction* eact = 0;
2850     {
2851         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2852         ege_output_action_set_use_markup( act, TRUE );
2853         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2854         g_object_set_data( holder, "mode_action", act );
2855     }
2857     /* Revolution */
2858     {
2859         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2860         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2861         eact = create_adjustment_action( "SpiralRevolutionAction",
2862                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
2863                                          "tools.shapes.spiral", "revolution", 3.0,
2864                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2865                                          0.01, 1024.0, 0.1, 1.0,
2866                                          labels, values, G_N_ELEMENTS(labels),
2867                                          sp_spl_tb_revolution_value_changed, 1, 2);
2868         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2869     }
2871     /* Expansion */
2872     {
2873         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2874         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2875         eact = create_adjustment_action( "SpiralExpansionAction",
2876                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2877                                          "tools.shapes.spiral", "expansion", 1.0,
2878                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2879                                          0.0, 1000.0, 0.01, 1.0,
2880                                          labels, values, G_N_ELEMENTS(labels),
2881                                          sp_spl_tb_expansion_value_changed);
2882         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2883     }
2885     /* T0 */
2886     {
2887         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2888         gdouble values[] = {0, 0.5, 0.9};
2889         eact = create_adjustment_action( "SpiralT0Action",
2890                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2891                                          "tools.shapes.spiral", "t0", 0.0,
2892                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2893                                          0.0, 0.999, 0.01, 1.0,
2894                                          labels, values, G_N_ELEMENTS(labels),
2895                                          sp_spl_tb_t0_value_changed);
2896         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2897     }
2899     /* Reset */
2900     {
2901         InkAction* inky = ink_action_new( "SpiralResetAction",
2902                                           _("Defaults"),
2903                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2904                                           GTK_STOCK_CLEAR,
2905                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2906         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2907         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2908     }
2911     sigc::connection *connection = new sigc::connection(
2912         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2913         );
2914     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2915     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2918 //########################
2919 //##     Pen/Pencil    ##
2920 //########################
2923 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2925     // Put stuff here
2928 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2930     // Put stuff here
2933 //########################
2934 //##       Tweak        ##
2935 //########################
2937 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2939     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2942 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2944     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2947 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
2949     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2952 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2954     int mode = ege_select_one_action_get_active( act );
2955     prefs_set_int_attribute("tools.tweak", "mode", mode);
2957     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2958     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2959     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2960     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2961     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2962     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2963     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
2964         if (doh) gtk_action_set_sensitive (doh, TRUE);
2965         if (dos) gtk_action_set_sensitive (dos, TRUE);
2966         if (dol) gtk_action_set_sensitive (dol, TRUE);
2967         if (doo) gtk_action_set_sensitive (doo, TRUE);
2968         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
2969         if (fid) gtk_action_set_sensitive (fid, FALSE);
2970     } else {
2971         if (doh) gtk_action_set_sensitive (doh, FALSE);
2972         if (dos) gtk_action_set_sensitive (dos, FALSE);
2973         if (dol) gtk_action_set_sensitive (dol, FALSE);
2974         if (doo) gtk_action_set_sensitive (doo, FALSE);
2975         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
2976         if (fid) gtk_action_set_sensitive (fid, TRUE);
2977     }
2980 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2982     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
2985 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
2986     bool show = gtk_toggle_action_get_active( act );
2987     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
2989 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
2990     bool show = gtk_toggle_action_get_active( act );
2991     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
2993 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
2994     bool show = gtk_toggle_action_get_active( act );
2995     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
2997 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
2998     bool show = gtk_toggle_action_get_active( act );
2999     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3002 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3004     {
3005         /* Width */
3006         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3007         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3008         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3009                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3010                                                               "tools.tweak", "width", 15,
3011                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3012                                                               1, 100, 1.0, 10.0,
3013                                                               labels, values, G_N_ELEMENTS(labels),
3014                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3015         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3016         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3017     }
3020     {
3021         /* Force */
3022         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3023         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3024         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3025                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3026                                                               "tools.tweak", "force", 20,
3027                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3028                                                               1, 100, 1.0, 10.0,
3029                                                               labels, values, G_N_ELEMENTS(labels),
3030                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3031         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3032         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3033     }
3035     /* Mode */
3036     {
3037         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3039         GtkTreeIter iter;
3040         gtk_list_store_append( model, &iter );
3041         gtk_list_store_set( model, &iter,
3042                             0, _("Push mode"),
3043                             1, _("Push parts of paths in any direction"),
3044                             2, "tweak_push_mode",
3045                             -1 );
3047         gtk_list_store_append( model, &iter );
3048         gtk_list_store_set( model, &iter,
3049                             0, _("Shrink mode"),
3050                             1, _("Shrink (inset) parts of paths"),
3051                             2, "tweak_shrink_mode",
3052                             -1 );
3054         gtk_list_store_append( model, &iter );
3055         gtk_list_store_set( model, &iter,
3056                             0, _("Grow mode"),
3057                             1, _("Grow (outset) parts of paths"),
3058                             2, "tweak_grow_mode",
3059                             -1 );
3061         gtk_list_store_append( model, &iter );
3062         gtk_list_store_set( model, &iter,
3063                             0, _("Attract mode"),
3064                             1, _("Attract parts of paths towards cursor"),
3065                             2, "tweak_attract_mode",
3066                             -1 );
3068         gtk_list_store_append( model, &iter );
3069         gtk_list_store_set( model, &iter,
3070                             0, _("Repel mode"),
3071                             1, _("Repel parts of paths from cursor"),
3072                             2, "tweak_repel_mode",
3073                             -1 );
3075         gtk_list_store_append( model, &iter );
3076         gtk_list_store_set( model, &iter,
3077                             0, _("Roughen mode"),
3078                             1, _("Roughen parts of paths"),
3079                             2, "tweak_roughen_mode",
3080                             -1 );
3082         gtk_list_store_append( model, &iter );
3083         gtk_list_store_set( model, &iter,
3084                             0, _("Color paint mode"),
3085                             1, _("Paint the tool's color upon selected objects"),
3086                             2, "tweak_colorpaint_mode",
3087                             -1 );
3089         gtk_list_store_append( model, &iter );
3090         gtk_list_store_set( model, &iter,
3091                             0, _("Color jitter mode"),
3092                             1, _("Jitter the colors of selected objects"),
3093                             2, "tweak_colorjitter_mode",
3094                             -1 );
3096         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3097         g_object_set( act, "short_label", _("Mode:"), NULL );
3098         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3099         g_object_set_data( holder, "mode_action", act );
3101         ege_select_one_action_set_appearance( act, "full" );
3102         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3103         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3104         ege_select_one_action_set_icon_column( act, 2 );
3105         ege_select_one_action_set_tooltip_column( act, 1  );
3107         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3108         ege_select_one_action_set_active( act, mode );
3109         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3111         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3112     }
3114     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3116     {
3117         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3118         ege_output_action_set_use_markup( act, TRUE );
3119         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3120         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3121             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3122         g_object_set_data( holder, "tweak_channels_label", act);
3123     }
3125     {
3126         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3127                                                       _("Hue"),
3128                                                       _("In color mode, act on objects' hue"),
3129                                                       NULL,
3130                                                       Inkscape::ICON_SIZE_DECORATION );
3131         g_object_set( act, "short_label", _("H"), NULL );
3132         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3133         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3134         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3135         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3136             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3137         g_object_set_data( holder, "tweak_doh", act);
3138     }
3139     {
3140         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3141                                                       _("Saturation"),
3142                                                       _("In color mode, act on objects' saturation"),
3143                                                       NULL,
3144                                                       Inkscape::ICON_SIZE_DECORATION );
3145         g_object_set( act, "short_label", _("S"), NULL );
3146         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3147         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3148         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3149         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3150             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3151         g_object_set_data( holder, "tweak_dos", act );
3152     }
3153     {
3154         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3155                                                       _("Lightness"),
3156                                                       _("In color mode, act on objects' lightness"),
3157                                                       NULL,
3158                                                       Inkscape::ICON_SIZE_DECORATION );
3159         g_object_set( act, "short_label", _("L"), NULL );
3160         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3161         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3162         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3163         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3164             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3165         g_object_set_data( holder, "tweak_dol", act );
3166     }
3167     {
3168         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3169                                                       _("Opacity"),
3170                                                       _("In color mode, act on objects' opacity"),
3171                                                       NULL,
3172                                                       Inkscape::ICON_SIZE_DECORATION );
3173         g_object_set( act, "short_label", _("O"), NULL );
3174         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3175         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3176         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3177         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3178             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3179         g_object_set_data( holder, "tweak_doo", act );
3180     }
3182     {   /* Fidelity */
3183         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3184         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3185         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3186                                                               _("Fidelity"), _("Fidelity:"),
3187                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3188                                                               "tools.tweak", "fidelity", 50,
3189                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3190                                                               1, 100, 1.0, 10.0,
3191                                                               labels, values, G_N_ELEMENTS(labels),
3192                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3193         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3194         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3195         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3196             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3197         g_object_set_data( holder, "tweak_fidelity", eact );
3198     }
3201     /* Use Pressure button */
3202     {
3203         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3204                                                       _("Pressure"),
3205                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3206                                                       "use_pressure",
3207                                                       Inkscape::ICON_SIZE_DECORATION );
3208         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3209         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3210         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3211     }
3216 //########################
3217 //##     Calligraphy    ##
3218 //########################
3220 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3222     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3225 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3227     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3230 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3232     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3235 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3237     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3240 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3242     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3245 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3247     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3250 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3252     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3255 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3257     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3260 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3262     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3265 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3267     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3270 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3272     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3274     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3277 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3279     // FIXME: make defaults settable via Inkscape Options
3280     struct KeyValue {
3281         char const *key;
3282         double value;
3283     } const key_values[] = {
3284         {"mass", 0.02},
3285         {"wiggle", 0.0},
3286         {"angle", 30.0},
3287         {"width", 15},
3288         {"thinning", 0.1},
3289         {"tremor", 0.0},
3290         {"flatness", 0.9},
3291         {"cap_rounding", 0.0}
3292     };
3294     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3295         KeyValue const &kv = key_values[i];
3296         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3297         if ( adj ) {
3298             gtk_adjustment_set_value(adj, kv.value);
3299         }
3300     }
3304 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3306     {
3307         EgeAdjustmentAction* calligraphy_angle = 0;
3309         {
3310         /* Width */
3311         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3312         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3313         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3314                                                               _("Pen Width"), _("Width:"),
3315                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3316                                                               "tools.calligraphic", "width", 15,
3317                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3318                                                               1, 100, 1.0, 10.0,
3319                                                               labels, values, G_N_ELEMENTS(labels),
3320                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3321         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3322         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3323         }
3325         {
3326         /* Thinning */
3327             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3328             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3329         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3330                                                               _("Stroke Thinning"), _("Thinning:"),
3331                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3332                                                               "tools.calligraphic", "thinning", 0.1,
3333                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3334                                                               -1.0, 1.0, 0.01, 0.1,
3335                                                               labels, values, G_N_ELEMENTS(labels),
3336                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3337         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3338         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3339         }
3341         {
3342         /* Angle */
3343         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3344         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3345         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3346                                                               _("Pen Angle"), _("Angle:"),
3347                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3348                                                               "tools.calligraphic", "angle", 30,
3349                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3350                                                               -90.0, 90.0, 1.0, 10.0,
3351                                                               labels, values, G_N_ELEMENTS(labels),
3352                                                               sp_ddc_angle_value_changed, 1, 0 );
3353         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3354         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3355         calligraphy_angle = eact;
3356         }
3358         {
3359         /* Fixation */
3360             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3361         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3362         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3363                                                               _("Fixation"), _("Fixation:"),
3364                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3365                                                               "tools.calligraphic", "flatness", 0.9,
3366                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3367                                                               0.0, 1.0, 0.01, 0.1,
3368                                                               labels, values, G_N_ELEMENTS(labels),
3369                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3370         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3371         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3372         }
3374         {
3375         /* Cap Rounding */
3376             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3377         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3378         // TRANSLATORS: "cap" means "end" (both start and finish) here
3379         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3380                                                               _("Cap rounding"), _("Caps:"),
3381                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3382                                                               "tools.calligraphic", "cap_rounding", 0.0,
3383                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3384                                                               0.0, 5.0, 0.01, 0.1,
3385                                                               labels, values, G_N_ELEMENTS(labels),
3386                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3387         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3388         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3389         }
3391         {
3392         /* Tremor */
3393             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3394         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3395         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3396                                                               _("Stroke Tremor"), _("Tremor:"),
3397                                                               _("Increase to make strokes rugged and trembling"),
3398                                                               "tools.calligraphic", "tremor", 0.0,
3399                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3400                                                               0.0, 1.0, 0.01, 0.1,
3401                                                               labels, values, G_N_ELEMENTS(labels),
3402                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3404         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3405         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3406         }
3408         {
3409         /* Wiggle */
3410         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3411         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3412         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3413                                                               _("Pen Wiggle"), _("Wiggle:"),
3414                                                               _("Increase to make the pen waver and wiggle"),
3415                                                               "tools.calligraphic", "wiggle", 0.0,
3416                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3417                                                               0.0, 1.0, 0.01, 0.1,
3418                                                               labels, values, G_N_ELEMENTS(labels),
3419                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3420         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3421         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3422         }
3424         {
3425         /* Mass */
3426             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3427         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3428         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3429                                                               _("Pen Mass"), _("Mass:"),
3430                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3431                                                               "tools.calligraphic", "mass", 0.02,
3432                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3433                                                               0.0, 1.0, 0.01, 0.1,
3434                                                               labels, values, G_N_ELEMENTS(labels),
3435                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3436         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3437         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3438         }
3441         /* Trace Background button */
3442         {
3443             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3444                                                           _("Trace Background"),
3445                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3446                                                           "trace_background",
3447                                                           Inkscape::ICON_SIZE_DECORATION );
3448             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3449             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3450             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3451         }
3453         /* Use Pressure button */
3454         {
3455             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3456                                                           _("Pressure"),
3457                                                           _("Use the pressure of the input device to alter the width of the pen"),
3458                                                           "use_pressure",
3459                                                           Inkscape::ICON_SIZE_DECORATION );
3460             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3461             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3462             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3463         }
3465         /* Use Tilt button */
3466         {
3467             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3468                                                           _("Tilt"),
3469                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3470                                                           "use_tilt",
3471                                                           Inkscape::ICON_SIZE_DECORATION );
3472             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3473             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3474             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3475             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3476         }
3478         /* Reset */
3479         {
3480             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3481                                              _("Defaults"),
3482                                              _("Reset all parameters to defaults"),
3483                                              GTK_STOCK_CLEAR );
3484             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3485             gtk_action_group_add_action( mainActions, act );
3486             gtk_action_set_sensitive( act, TRUE );
3487         }
3488     }
3492 //########################
3493 //##    Circle / Arc    ##
3494 //########################
3496 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3498     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3499     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3501     if (v1 == 0 && v2 == 0) {
3502         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3503             gtk_action_set_sensitive( ocb, FALSE );
3504             gtk_action_set_sensitive( make_whole, FALSE );
3505         }
3506     } else {
3507         gtk_action_set_sensitive( ocb, TRUE );
3508         gtk_action_set_sensitive( make_whole, TRUE );
3509     }
3512 static void
3513 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3515     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3517     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3518         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3519     }
3521     // quit if run by the attr_changed listener
3522     if (g_object_get_data( tbl, "freeze" )) {
3523         return;
3524     }
3526     // in turn, prevent listener from responding
3527     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3529     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3531     bool modmade = false;
3532     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3533          items != NULL;
3534          items = items->next)
3535     {
3536         SPItem *item = SP_ITEM(items->data);
3538         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3540             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3541             SPArc *arc = SP_ARC(item);
3543             if (!strcmp(value_name, "start"))
3544                 ge->start = (adj->value * M_PI)/ 180;
3545             else
3546                 ge->end = (adj->value * M_PI)/ 180;
3548             sp_genericellipse_normalize(ge);
3549             ((SPObject *)arc)->updateRepr();
3550             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3552             modmade = true;
3553         }
3554     }
3556     g_free(namespaced_name);
3558     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3560     sp_arctb_sensitivize( tbl, adj->value, other->value );
3562     if (modmade) {
3563         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3564                                    _("Arc: Change start/end"));
3565     }
3567     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3571 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3573     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3576 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3578     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3581 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3583     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3584     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3585         if ( ege_select_one_action_get_active( act ) != 0 ) {
3586             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3587         } else {
3588             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3589         }
3590     }
3592     // quit if run by the attr_changed listener
3593     if (g_object_get_data( tbl, "freeze" )) {
3594         return;
3595     }
3597     // in turn, prevent listener from responding
3598     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3600     bool modmade = false;
3602     if ( ege_select_one_action_get_active(act) != 0 ) {
3603         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3604              items != NULL;
3605              items = items->next)
3606         {
3607             if (SP_IS_ARC((SPItem *) items->data)) {
3608                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3609                 repr->setAttribute("sodipodi:open", "true");
3610                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3611                 modmade = true;
3612             }
3613         }
3614     } else {
3615         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3616              items != NULL;
3617              items = items->next)
3618         {
3619             if (SP_IS_ARC((SPItem *) items->data))    {
3620                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3621                 repr->setAttribute("sodipodi:open", NULL);
3622                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3623                 modmade = true;
3624             }
3625         }
3626     }
3628     if (modmade) {
3629         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3630                                    _("Arc: Change open/closed"));
3631     }
3633     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3636 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3638     GtkAdjustment *adj;
3639     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3640     gtk_adjustment_set_value(adj, 0.0);
3641     gtk_adjustment_value_changed(adj);
3643     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3644     gtk_adjustment_set_value(adj, 0.0);
3645     gtk_adjustment_value_changed(adj);
3647     spinbutton_defocus( GTK_OBJECT(obj) );
3650 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3651                                       gchar const */*old_value*/, gchar const */*new_value*/,
3652                                       bool /*is_interactive*/, gpointer data)
3654     GObject *tbl = G_OBJECT(data);
3656     // quit if run by the _changed callbacks
3657     if (g_object_get_data( tbl, "freeze" )) {
3658         return;
3659     }
3661     // in turn, prevent callbacks from responding
3662     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3664     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3665     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3667     GtkAdjustment *adj1,*adj2;
3668     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3669     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3670     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3671     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3673     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3675     char const *openstr = NULL;
3676     openstr = repr->attribute("sodipodi:open");
3677     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3679     if (openstr) {
3680         ege_select_one_action_set_active( ocb, 1 );
3681     } else {
3682         ege_select_one_action_set_active( ocb, 0 );
3683     }
3685     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3688 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3689     NULL, /* child_added */
3690     NULL, /* child_removed */
3691     arc_tb_event_attr_changed,
3692     NULL, /* content_changed */
3693     NULL  /* order_changed */
3694 };
3697 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3699     int n_selected = 0;
3700     Inkscape::XML::Node *repr = NULL;
3702     purge_repr_listener( tbl, tbl );
3704     for (GSList const *items = selection->itemList();
3705          items != NULL;
3706          items = items->next)
3707     {
3708         if (SP_IS_ARC((SPItem *) items->data)) {
3709             n_selected++;
3710             repr = SP_OBJECT_REPR((SPItem *) items->data);
3711         }
3712     }
3714     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3716     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3717     if (n_selected == 0) {
3718         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3719     } else if (n_selected == 1) {
3720         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3721         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3723         if (repr) {
3724             g_object_set_data( tbl, "repr", repr );
3725             Inkscape::GC::anchor(repr);
3726             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3727             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3728         }
3729     } else {
3730         // FIXME: implement averaging of all parameters for multiple selected
3731         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3732         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3733         sp_arctb_sensitivize( tbl, 1, 0 );
3734     }
3738 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3740     EgeAdjustmentAction* eact = 0;
3743     {
3744         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3745         ege_output_action_set_use_markup( act, TRUE );
3746         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3747         g_object_set_data( holder, "mode_action", act );
3748     }
3750     /* Start */
3751     {
3752         eact = create_adjustment_action( "ArcStartAction",
3753                                          _("Start"), _("Start:"),
3754                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
3755                                          "tools.shapes.arc", "start", 0.0,
3756                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3757                                          -360.0, 360.0, 1.0, 10.0,
3758                                          0, 0, 0,
3759                                          sp_arctb_start_value_changed);
3760         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3761     }
3763     /* End */
3764     {
3765         eact = create_adjustment_action( "ArcEndAction",
3766                                          _("End"), _("End:"),
3767                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
3768                                          "tools.shapes.arc", "end", 0.0,
3769                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3770                                          -360.0, 360.0, 1.0, 10.0,
3771                                          0, 0, 0,
3772                                          sp_arctb_end_value_changed);
3773         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3774     }
3776     /* Segments / Pie checkbox */
3777     {
3778         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3780         GtkTreeIter iter;
3781         gtk_list_store_append( model, &iter );
3782         gtk_list_store_set( model, &iter,
3783                             0, _("Closed arc"),
3784                             1, _("Switch to segment (closed shape with two radii)"),
3785                             2, "circle_closed_arc",
3786                             -1 );
3788         gtk_list_store_append( model, &iter );
3789         gtk_list_store_set( model, &iter,
3790                             0, _("Open Arc"),
3791                             1, _("Switch to arc (unclosed shape)"),
3792                             2, "circle_open_arc",
3793                             -1 );
3795         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3796         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3797         g_object_set_data( holder, "open_action", act );
3799         ege_select_one_action_set_appearance( act, "full" );
3800         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3801         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3802         ege_select_one_action_set_icon_column( act, 2 );
3803         ege_select_one_action_set_tooltip_column( act, 1  );
3805         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3806         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3807         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3808         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3809     }
3811     /* Make Whole */
3812     {
3813         InkAction* inky = ink_action_new( "ArcResetAction",
3814                                           _("Make whole"),
3815                                           _("Make the shape a whole ellipse, not arc or segment"),
3816                                           "reset_circle",
3817                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3818         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3819         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3820         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3821         g_object_set_data( holder, "make_whole", inky );
3822     }
3824     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3825     // sensitivize make whole and open checkbox
3826     {
3827         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3828         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3829         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3830     }
3833     sigc::connection *connection = new sigc::connection(
3834         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3835         );
3836     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3837     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3843 // toggle button callbacks and updaters
3845 //########################
3846 //##      Dropper       ##
3847 //########################
3849 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3850     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3851     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3852     if ( set_action ) {
3853         if ( gtk_toggle_action_get_active( act ) ) {
3854             gtk_action_set_sensitive( set_action, TRUE );
3855         } else {
3856             gtk_action_set_sensitive( set_action, FALSE );
3857         }
3858     }
3860     spinbutton_defocus(GTK_OBJECT(tbl));
3863 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3864     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3865     spinbutton_defocus(GTK_OBJECT(tbl));
3869 /**
3870  * Dropper auxiliary toolbar construction and setup.
3871  *
3872  * TODO: Would like to add swatch of current color.
3873  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3874  *       can drag and drop places. Will provide a nice mixing palette.
3875  */
3876 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3878     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3880     {
3881         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3882                                                       _("Pick alpha"),
3883                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3884                                                       "color_alpha_get",
3885                                                       Inkscape::ICON_SIZE_DECORATION );
3886         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3887         g_object_set_data( holder, "pick_action", act );
3888         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3889         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3890     }
3892     {
3893         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3894                                                       _("Set alpha"),
3895                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3896                                                       "color_alpha_set",
3897                                                       Inkscape::ICON_SIZE_DECORATION );
3898         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3899         g_object_set_data( holder, "set_action", act );
3900         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3901         // make sure it's disabled if we're not picking alpha
3902         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3903         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3904     }
3908 //########################
3909 //##    Text Toolbox    ##
3910 //########################
3911 /*
3912 static void
3913 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3915     //Call back for letter sizing spinbutton
3918 static void
3919 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3921     //Call back for line height spinbutton
3924 static void
3925 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3927     //Call back for horizontal kerning spinbutton
3930 static void
3931 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3933     //Call back for vertical kerning spinbutton
3936 static void
3937 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3939     //Call back for letter rotation spinbutton
3940 }*/
3942 namespace {
3944 bool visible = false;
3946 void
3947 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
3949     SPStyle *query =
3950         sp_style_new (SP_ACTIVE_DOCUMENT);
3952     int result_family =
3953         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3955     int result_style =
3956         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3958     int result_numbers =
3959         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3961     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3963     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3964     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3965     {
3966         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3968         if (repr)
3969         {
3970             sp_style_read_from_repr (query, repr);
3971         }
3972         else
3973         {
3974             return;
3975         }
3976     }
3978     if (query->text)
3979     {
3980         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
3981             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3982             gtk_entry_set_text (GTK_ENTRY (entry), "");
3984         } else if (query->text->font_family.value) {
3986             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3987             gtk_entry_set_text (GTK_ENTRY (entry), query->text->font_family.value);
3989             Gtk::TreePath path;
3990             try {
3991                 path = Inkscape::FontLister::get_instance()->get_row_for_font (query->text->font_family.value);
3992             } catch (...) {
3993                 return;
3994             }
3996             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3997             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3999             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4001             gtk_tree_selection_select_path (tselection, path.gobj());
4002             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4004             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4005         }
4007         //Size
4008         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4009         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4010         g_object_set_data (tbl, "size-block", gpointer(1));
4011         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4012         g_object_set_data (tbl, "size-block", gpointer(0));
4013         free (str);
4015         //Anchor
4016         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4017         {
4018             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4019             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4020             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4021             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4022         }
4023         else
4024         {
4025             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4026             {
4027                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4028                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4029                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4030                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4031             }
4032             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4033             {
4034                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4035                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4036                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4037                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4038             }
4039             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4040             {
4041                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4042                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4043                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4044                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4045             }
4046         }
4048         //Style
4049         {
4050             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4052             gboolean active = gtk_toggle_button_get_active (button);
4053             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4055             if (active != check)
4056             {
4057                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4058                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4059                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4060             }
4061         }
4063         {
4064             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4066             gboolean active = gtk_toggle_button_get_active (button);
4067             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4069             if (active != check)
4070             {
4071                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4072                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4073                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4074             }
4075         }
4077         //Orientation
4078         //locking both buttons, changing one affect all group (both)
4079         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4080         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4082         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4083         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4085         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4086         {
4087             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4088         }
4089         else
4090         {
4091             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4092         }
4093         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4094         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4095     }
4097     sp_style_unref(query);
4100 void
4101 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4103     sp_text_toolbox_selection_changed (selection, tbl);
4106 void
4107 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4109     sp_text_toolbox_selection_changed (NULL, tbl);
4112 void
4113 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4114                                 GObject             *tbl)
4116     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4117     GtkTreeModel *model = 0;
4118     GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4119     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4120     GtkTreeIter   iter;
4121     char         *family = 0;
4123     (void)popdown;
4125     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4126     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4128     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4129         return;
4130     }
4132     gtk_tree_model_get (model, &iter, 0, &family, -1);
4134     if (g_object_get_data (G_OBJECT (selection), "block"))
4135     {
4136         gtk_entry_set_text (GTK_ENTRY (entry), family);
4137         return;
4138     }
4140     gtk_entry_set_text (GTK_ENTRY (entry), family);
4142     SPStyle *query =
4143         sp_style_new (SP_ACTIVE_DOCUMENT);
4145     int result_numbers =
4146         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4148     SPCSSAttr *css = sp_repr_css_attr_new ();
4149     sp_repr_css_set_property (css, "font-family", family);
4151     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4152     if (result_numbers == QUERY_STYLE_NOTHING)
4153     {
4154         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4155         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4156     }
4157     else
4158     {
4159         sp_desktop_set_style (desktop, css, true, true);
4160     }
4162     sp_style_unref(query);
4164     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4165                                    _("Text: Change font family"));
4166     sp_repr_css_attr_unref (css);
4167     free (family);
4168     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4170     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4173 void
4174 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4175                                        GObject      *tbl)
4177     const char *family = gtk_entry_get_text (entry);
4179     try {
4180         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4181         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4182         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4183         gtk_tree_selection_select_path (selection, path.gobj());
4184         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4185         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4186     } catch (...) {
4187         if (family && strlen (family))
4188         {
4189             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4190         }
4191     }
4194 void
4195 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4196                                    gpointer          data)
4198     if (g_object_get_data (G_OBJECT (button), "block")) return;
4199     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4200     int prop = GPOINTER_TO_INT(data);
4202     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4203     SPCSSAttr *css = sp_repr_css_attr_new ();
4205     switch (prop)
4206     {
4207         case 0:
4208         {
4209             sp_repr_css_set_property (css, "text-anchor", "start");
4210             sp_repr_css_set_property (css, "text-align", "start");
4211             break;
4212         }
4213         case 1:
4214         {
4215             sp_repr_css_set_property (css, "text-anchor", "middle");
4216             sp_repr_css_set_property (css, "text-align", "center");
4217             break;
4218         }
4220         case 2:
4221         {
4222             sp_repr_css_set_property (css, "text-anchor", "end");
4223             sp_repr_css_set_property (css, "text-align", "end");
4224             break;
4225         }
4227         case 3:
4228         {
4229             sp_repr_css_set_property (css, "text-anchor", "start");
4230             sp_repr_css_set_property (css, "text-align", "justify");
4231             break;
4232         }
4233     }
4235     SPStyle *query =
4236         sp_style_new (SP_ACTIVE_DOCUMENT);
4237     int result_numbers =
4238         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4240     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4241     if (result_numbers == QUERY_STYLE_NOTHING)
4242     {
4243         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4244     }
4246     sp_style_unref(query);
4248     sp_desktop_set_style (desktop, css, true, true);
4249     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4250                                    _("Text: Change alignment"));
4251     sp_repr_css_attr_unref (css);
4253     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4256 void
4257 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4258                                gpointer          data)
4260     if (g_object_get_data (G_OBJECT (button), "block")) return;
4262     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4263     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4264     int          prop       = GPOINTER_TO_INT(data);
4265     bool         active     = gtk_toggle_button_get_active (button);
4268     switch (prop)
4269     {
4270         case 0:
4271         {
4272             sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4273             break;
4274         }
4276         case 1:
4277         {
4278             sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4279             break;
4280         }
4281     }
4283     SPStyle *query =
4284         sp_style_new (SP_ACTIVE_DOCUMENT);
4285     int result_numbers =
4286         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4288     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4289     if (result_numbers == QUERY_STYLE_NOTHING)
4290     {
4291         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4292     }
4294     sp_style_unref(query);
4296     sp_desktop_set_style (desktop, css, true, true);
4297     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4298                                    _("Text: Change font style"));
4299     sp_repr_css_attr_unref (css);
4301     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4304 void
4305 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4306                                      gpointer         data)
4308     if (g_object_get_data (G_OBJECT (button), "block")) {
4309         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4310         return;
4311     }
4313     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4314     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4315     int          prop       = GPOINTER_TO_INT(data);
4317     switch (prop)
4318     {
4319         case 0:
4320         {
4321             sp_repr_css_set_property (css, "writing-mode", "lr");
4322             break;
4323         }
4325         case 1:
4326         {
4327             sp_repr_css_set_property (css, "writing-mode", "tb");
4328             break;
4329         }
4330     }
4332     SPStyle *query =
4333         sp_style_new (SP_ACTIVE_DOCUMENT);
4334     int result_numbers =
4335         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4337     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4338     if (result_numbers == QUERY_STYLE_NOTHING)
4339     {
4340         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4341     }
4343     sp_desktop_set_style (desktop, css, true, true);
4344     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4345                                    _("Text: Change orientation"));
4346     sp_repr_css_attr_unref (css);
4348     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4351 gboolean
4352 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, gpointer /*data*/)
4354     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4355     if (!desktop) return FALSE;
4357     switch (get_group0_keyval (event)) {
4358         case GDK_Escape: // defocus
4359             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4360             return TRUE; // I consumed the event
4361             break;
4362         case GDK_Return: // defocus
4363         case GDK_KP_Enter:
4364             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4365             return TRUE; // I consumed the event
4366             break;
4367     }
4368     return FALSE;
4371 gboolean
4372 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4374     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4375     if (!desktop) return FALSE;
4377     switch (get_group0_keyval (event)) {
4378         case GDK_Escape: // defocus
4379             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4380             sp_text_toolbox_selection_changed (NULL, tbl); // update
4381             return TRUE; // I consumed the event
4382             break;
4383     }
4384     return FALSE;
4387 gboolean
4388 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4390     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4391     if (!desktop) return FALSE;
4393     switch (get_group0_keyval (event)) {
4394         case GDK_KP_Enter:
4395         case GDK_Return:
4396         case GDK_Escape: // defocus
4397             gtk_widget_hide (w);
4398             visible = false;
4399             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4400             return TRUE; // I consumed the event
4401             break;
4402     }
4403     return FALSE;
4407 void
4408 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4409                                GObject     *tbl)
4411     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4413     if (g_object_get_data (tbl, "size-block")) return;
4415     char *text = gtk_combo_box_get_active_text (cbox);
4417     SPCSSAttr *css = sp_repr_css_attr_new ();
4418     sp_repr_css_set_property (css, "font-size", text);
4419     free (text);
4421     SPStyle *query =
4422         sp_style_new (SP_ACTIVE_DOCUMENT);
4423     int result_numbers =
4424         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4426     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4427     if (result_numbers == QUERY_STYLE_NOTHING)
4428     {
4429         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4430     }
4432     sp_style_unref(query);
4434     sp_desktop_set_style (desktop, css, true, true);
4435     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4436                                    _("Text: Change font size"));
4437     sp_repr_css_attr_unref (css);
4440     if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
4441         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4444 void
4445 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4446                                          GObject            *tbl)
4448     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4449     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4450     int x, y;
4452     if (!visible)
4453     {
4454         gdk_window_get_origin (widget->window, &x, &y);
4455         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4456         gtk_widget_show_all (popdown);
4458         gdk_pointer_grab (widget->window, TRUE,
4459                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4460                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4461                                         GDK_POINTER_MOTION_MASK),
4462                           NULL, NULL, GDK_CURRENT_TIME);
4464         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4466         visible = true;
4467     }
4468     else
4469     {
4470         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4471         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4472         gtk_widget_hide (popdown);
4473         visible = false;
4474     }
4477 gboolean
4478 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4479                                  GdkEventFocus    */*event*/,
4480                                  GObject          */*tbl*/)
4482     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4483     return FALSE;
4486 gboolean
4487 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4488                                    GdkEventFocus    */*event*/,
4489                                    GObject          */*tbl*/)
4491     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4493     gtk_widget_hide (popdown);
4494     visible = false;
4495     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4496     return TRUE;
4499 void
4500 cell_data_func  (GtkTreeViewColumn */*column*/,
4501                  GtkCellRenderer   *cell,
4502                  GtkTreeModel      *tree_model,
4503                  GtkTreeIter       *iter,
4504                  gpointer           /*data*/)
4506     char        *family,
4507         *family_escaped,
4508         *sample_escaped;
4510     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4512     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4514     family_escaped = g_markup_escape_text (family, -1);
4515     sample_escaped = g_markup_escape_text (sample, -1);
4517     std::stringstream markup;
4518     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4519     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4521     free (family);
4522     free (family_escaped);
4523     free (sample_escaped);
4526 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4527     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4528     if (completion) {
4529         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4530         g_object_unref (completion);
4531     }
4534 GtkWidget*
4535 sp_text_toolbox_new (SPDesktop *desktop)
4537     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4539     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4540     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4542     GtkTooltips *tt = gtk_tooltips_new();
4543     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4545     ////////////Family
4546     //Window
4547     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4548     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4550     //Entry
4551     GtkWidget           *entry = gtk_entry_new ();
4552     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4553     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4554     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4555     gtk_entry_completion_set_text_column (completion, 0);
4556     gtk_entry_completion_set_minimum_key_length (completion, 1);
4557     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4558     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4559     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4560     aux_toolbox_space (tbl, 1);
4561     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4562     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4564     //Button
4565     GtkWidget   *button = gtk_button_new ();
4566     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4567     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4569     //Popdown
4570     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4571     GtkWidget           *treeview = gtk_tree_view_new ();
4573     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4574     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4575     gtk_tree_view_column_pack_start (column, cell, FALSE);
4576     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4577     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4578     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4580     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4581     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4582     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4584     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4586     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4587     gtk_container_add (GTK_CONTAINER (sw), treeview);
4589     gtk_container_add (GTK_CONTAINER (window), sw);
4590     gtk_widget_set_size_request (window, 300, 450);
4592     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4593     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4594     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4596     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4598     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4599     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4601     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4602     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4604     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4605     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4606     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4607     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4608     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4610     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4611     aux_toolbox_space (tbl, 1);
4612     GtkWidget *box = gtk_event_box_new ();
4613     gtk_container_add (GTK_CONTAINER (box), image);
4614     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4615     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4616     GtkTooltips *tooltips = gtk_tooltips_new ();
4617     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4618     gtk_widget_hide (GTK_WIDGET (box));
4619     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4621     ////////////Size
4622     const char *sizes[] = {
4623         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4624         "16", "18", "20", "22", "24", "28",
4625         "32", "36", "40", "48", "56", "64", "72", "144"
4626     };
4628     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4629     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4630     gtk_widget_set_size_request (cbox, 80, -1);
4631     aux_toolbox_space (tbl, 1);
4632     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4633     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4634     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4635     gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
4637     //spacer
4638     aux_toolbox_space (tbl, 4);
4639     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4641     ////////////Text anchor
4642     GtkWidget *group   = gtk_radio_button_new (NULL);
4643     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4644     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4646     // left
4647     GtkWidget *rbutton = group;
4648     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4649     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4650     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4652     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4653     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4654     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4655     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4657     // center
4658     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4659     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4660     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4661     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4663     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4664     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
4665     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4666     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4668     // right
4669     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4670     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4671     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4672     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4674     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4675     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
4676     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4677     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4679     // fill
4680     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4681     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4682     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4683     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4685     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4686     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
4687     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4688     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4690     aux_toolbox_space (tbl, 1);
4691     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4693     //spacer
4694     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4696     ////////////Text style
4697     row = gtk_hbox_new (FALSE, 4);
4699     // bold
4700     rbutton = gtk_toggle_button_new ();
4701     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4702     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4703     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4704     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4706     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4707     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
4708     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4710     // italic
4711     rbutton = gtk_toggle_button_new ();
4712     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4713     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4714     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4715     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4717     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4718     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
4719     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4721     aux_toolbox_space (tbl, 1);
4722     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4724     //spacer
4725     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4727     ////////////Text orientation
4728     group   = gtk_radio_button_new (NULL);
4729     row     = gtk_hbox_new (FALSE, 4);
4730     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4732     // horizontal
4733     rbutton = group;
4734     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4735     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4736     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4737     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4739     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4740     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4741     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4743     // vertical
4744     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4745     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4746     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4747     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4748     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4750     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4751     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4752     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4753     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4756     //watch selection
4757     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4759     sigc::connection *c_selection_changed =
4760         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4761                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4762     pool->add_connection ("selection-changed", c_selection_changed);
4764     sigc::connection *c_selection_modified =
4765         new sigc::connection (sp_desktop_selection (desktop)->connectModified
4766                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4767     pool->add_connection ("selection-modified", c_selection_modified);
4769     sigc::connection *c_subselection_changed =
4770         new sigc::connection (desktop->connectToolSubselectionChanged
4771                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4772     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4774     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4777     gtk_widget_show_all (tbl);
4778     return tbl;
4780 } // end of sp_text_toolbox_new()
4782 }//<unnamed> namespace
4785 //#########################
4786 //##      Connector      ##
4787 //#########################
4789 static void sp_connector_path_set_avoid(void)
4791     cc_selection_set_avoid(true);
4795 static void sp_connector_path_set_ignore(void)
4797     cc_selection_set_avoid(false);
4802 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4804     // quit if run by the _changed callbacks
4805     if (g_object_get_data( tbl, "freeze" )) {
4806         return;
4807     }
4809     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4810     SPDocument *doc = sp_desktop_document(desktop);
4812     if (!sp_document_get_undo_sensitive(doc))
4813     {
4814         return;
4815     }
4817     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4819     if ( repr->attribute("inkscape:connector-spacing") ) {
4820         gdouble priorValue = gtk_adjustment_get_value(adj);
4821         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4822         if ( priorValue == gtk_adjustment_get_value(adj) ) {
4823             return;
4824         }
4825     } else if ( adj->value == defaultConnSpacing ) {
4826         return;
4827     }
4829     // in turn, prevent callbacks from responding
4830     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4832     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4833     SP_OBJECT(desktop->namedview)->updateRepr();
4835     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4836     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4837         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4838         NR::Matrix m = NR::identity();
4839         avoid_item_move(&m, item);
4840     }
4842     if (items) {
4843         g_slist_free(items);
4844     }
4846     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4847             _("Change connector spacing"));
4849     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4851     spinbutton_defocus(GTK_OBJECT(tbl));
4854 static void sp_connector_graph_layout(void)
4856     if (!SP_ACTIVE_DESKTOP) return;
4858     // hack for clones, see comment in align-and-distribute.cpp
4859     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4860     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4862     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
4864     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
4866     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
4869 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4871     if ( gtk_toggle_action_get_active( act ) ) {
4872         prefs_set_string_attribute("tools.connector", "directedlayout",
4873                 "true");
4874     } else {
4875         prefs_set_string_attribute("tools.connector", "directedlayout",
4876                 "false");
4877     }
4880 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4882     if ( gtk_toggle_action_get_active( act ) ) {
4883         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4884                 "true");
4885     } else {
4886         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4887                 "false");
4888     }
4892 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
4894     prefs_set_double_attribute("tools.connector", "length", adj->value);
4895     spinbutton_defocus(GTK_OBJECT(tbl));
4898 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
4899                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
4900                                             bool /*is_interactive*/, gpointer data)
4902     GtkWidget *tbl = GTK_WIDGET(data);
4904     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
4905         return;
4906     }
4907     if (strcmp(name, "inkscape:connector-spacing") != 0) {
4908         return;
4909     }
4911     GtkAdjustment *adj = (GtkAdjustment*)
4912             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
4913     gdouble spacing = defaultConnSpacing;
4914     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
4916     gtk_adjustment_set_value(adj, spacing);
4920 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
4921     NULL, /* child_added */
4922     NULL, /* child_removed */
4923     connector_tb_event_attr_changed,
4924     NULL, /* content_changed */
4925     NULL  /* order_changed */
4926 };
4929 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
4931     {
4932         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
4933                                           _("Avoid"),
4934                                           _("Make connectors avoid selected objects"),
4935                                           "connector_avoid",
4936                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4937         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
4938         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4939     }
4941     {
4942         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
4943                                           _("Ignore"),
4944                                           _("Make connectors ignore selected objects"),
4945                                           "connector_ignore",
4946                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4947         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
4948         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4949     }
4951     EgeAdjustmentAction* eact = 0;
4953     // Spacing spinbox
4954     eact = create_adjustment_action( "ConnectorSpacingAction",
4955                                      _("Connector Spacing"), _("Spacing:"),
4956                                      _("The amount of space left around objects by auto-routing connectors"),
4957                                      "tools.connector", "spacing", defaultConnSpacing,
4958                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
4959                                      0, 100, 1.0, 10.0,
4960                                      0, 0, 0,
4961                                      connector_spacing_changed, 1, 0 );
4962     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4964     // Graph (connector network) layout
4965     {
4966         InkAction* inky = ink_action_new( "ConnectorGraphAction",
4967                                           _("Graph"),
4968                                           _("Nicely arrange selected connector network"),
4969                                           "graph_layout",
4970                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4971         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
4972         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4973     }
4975     // Default connector length spinbox
4976     eact = create_adjustment_action( "ConnectorLengthAction",
4977                                      _("Connector Length"), _("Length:"),
4978                                      _("Ideal length for connectors when layout is applied"),
4979                                      "tools.connector", "length", 100,
4980                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
4981                                      10, 1000, 10.0, 100.0,
4982                                      0, 0, 0,
4983                                      connector_length_changed, 1, 0 );
4984     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4987     // Directed edges toggle button
4988     {
4989         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
4990                                                       _("Downwards"),
4991                                                       _("Make connectors with end-markers (arrows) point downwards"),
4992                                                       "directed_graph",
4993                                                       Inkscape::ICON_SIZE_DECORATION );
4994         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4996         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
4997         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
4998                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5000         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5001     }
5003     // Avoid overlaps toggle button
5004     {
5005         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5006                                                       _("Remove overlaps"),
5007                                                       _("Do not allow overlapping shapes"),
5008                                                       "remove_overlaps",
5009                                                       Inkscape::ICON_SIZE_DECORATION );
5010         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5012         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5013         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5014                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5016         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5017     }
5019     // Code to watch for changes to the connector-spacing attribute in
5020     // the XML.
5021     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5022     g_assert(repr != NULL);
5024     purge_repr_listener( holder, holder );
5026     if (repr) {
5027         g_object_set_data( holder, "repr", repr );
5028         Inkscape::GC::anchor(repr);
5029         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5030         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5031     }
5032 } // end of sp_connector_toolbox_prep()
5035 //#########################
5036 //##     Paintbucket     ##
5037 //#########################
5039 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5041     gint channels = ege_select_one_action_get_active( act );
5042     flood_channels_set_channels( channels );
5045 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5047     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5050 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5052     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5055 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5057     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5058     SPUnit const *unit = tracker->getActiveUnit();
5060     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5062     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5065 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5067     // FIXME: make defaults settable via Inkscape Options
5068     struct KeyValue {
5069         char const *key;
5070         double value;
5071     } const key_values[] = {
5072         {"threshold", 15},
5073         {"offset", 0.0}
5074     };
5076     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5077         KeyValue const &kv = key_values[i];
5078         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5079         if ( adj ) {
5080             gtk_adjustment_set_value(adj, kv.value);
5081         }
5082     }
5084     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5085     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5086     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5087     ege_select_one_action_set_active( autogap_action, 0 );
5090 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5092     EgeAdjustmentAction* eact = 0;
5094     {
5095         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5097         GList* items = 0;
5098         gint count = 0;
5099         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5100         {
5101             GtkTreeIter iter;
5102             gtk_list_store_append( model, &iter );
5103             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5104             count++;
5105         }
5106         g_list_free( items );
5107         items = 0;
5108         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5109         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5110         ege_select_one_action_set_appearance( act1, "compact" );
5111         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5112         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5113         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5114         g_object_set_data( holder, "channels_action", act1 );
5115     }
5117     // Spacing spinbox
5118     {
5119         eact = create_adjustment_action(
5120             "ThresholdAction",
5121             _("Fill Threshold"), _("Threshold:"),
5122             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5123             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5124             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5125             0, 0, 0,
5126             paintbucket_threshold_changed, 1, 0 );
5128         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5129     }
5131     // Create the units menu.
5132     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5133     tracker->setActiveUnit(sp_unit_get_by_abbreviation(prefs_get_string_attribute("tools.paintbucket", "offsetunits")));
5134     g_object_set_data( holder, "tracker", tracker );
5135     {
5136         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5137         gtk_action_group_add_action( mainActions, act );
5138     }
5140     // Offset spinbox
5141     {
5142         eact = create_adjustment_action(
5143             "OffsetAction",
5144             _("Grow/shrink by"), _("Grow/shrink by:"),
5145             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5146             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5147             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5148             0, 0, 0,
5149             paintbucket_offset_changed, 1, 2);
5150         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5152         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5153     }
5155     /* Auto Gap */
5156     {
5157         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5159         GList* items = 0;
5160         gint count = 0;
5161         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5162         {
5163             GtkTreeIter iter;
5164             gtk_list_store_append( model, &iter );
5165             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5166             count++;
5167         }
5168         g_list_free( items );
5169         items = 0;
5170         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5171         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5172         ege_select_one_action_set_appearance( act2, "compact" );
5173         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5174         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5175         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5176         g_object_set_data( holder, "autogap_action", act2 );
5177     }
5179     /* Reset */
5180     {
5181         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5182                                           _("Defaults"),
5183                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5184                                           GTK_STOCK_CLEAR );
5185         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5186         gtk_action_group_add_action( mainActions, act );
5187         gtk_action_set_sensitive( act, TRUE );
5188     }
5192 /*
5193   Local Variables:
5194   mode:c++
5195   c-file-style:"stroustrup"
5196   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5197   indent-tabs-mode:nil
5198   fill-column:99
5199   End:
5200 */
5201 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :