Code

Warning cleanup
[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       sp_3dbox_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 //    { "SP3DBoxContext",    "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     { "SP3DBoxContext",  "3dbox_toolbox",  0, sp_3dbox_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 sp_3dbox_toggle_vp_changed (GtkToggleAction */*act*/, GObject *dataKludge, Box3D::Axis axis)
2348     SPDesktop *desktop = (SPDesktop *) g_object_get_data (dataKludge, "desktop");
2349     SPDocument *document = sp_desktop_document (desktop);
2350     Box3D::Perspective3D *persp = document->current_perspective;
2352     g_return_if_fail (is_single_axis_direction (axis));
2353     g_return_if_fail (persp);
2355     persp->toggle_boxes (axis);
2357     gchar *str;
2358     switch (axis) {
2359         case Box3D::X:
2360             str = g_strdup ("box3d_angle_x_action");
2361             break;
2362         case Box3D::Y:
2363             str = g_strdup ("box3d_angle_y_action");
2364             break;
2365         case Box3D::Z:
2366             str = g_strdup ("box3d_angle_z_action");
2367             break;
2368         default:
2369             return;
2370     }
2371     GtkAction* angle_action = GTK_ACTION (g_object_get_data (dataKludge, str));
2372     if (angle_action) {
2373         gtk_action_set_sensitive (angle_action, !persp->get_vanishing_point (axis)->is_finite() );
2374     }
2376     // FIXME: Given how it is realized in the other tools, this is probably not the right way to do it,
2377     //        but without the if construct, we get continuous segfaults. Needs further investigation.
2378     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2379         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,
2380                          _("3D Box: Change perspective"));
2381     }
2384 static void sp_3dbox_toggle_vp_x_changed(GtkToggleAction *act, GObject *dataKludge)
2386     sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::X);
2389 static void sp_3dbox_toggle_vp_y_changed(GtkToggleAction *act, GObject *dataKludge)
2391     sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::Y);
2394 static void sp_3dbox_toggle_vp_z_changed(GtkToggleAction *act, GObject *dataKludge)
2396     sp_3dbox_toggle_vp_changed (act, dataKludge, Box3D::Z);
2399 static void sp_3dbox_vp_angle_changed(GtkAdjustment *adj, GObject *dataKludge, Box3D::Axis axis )
2401     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
2402     Box3D::Perspective3D *persp = sp_desktop_document (desktop)->current_perspective;
2404     if (persp) {
2405         double angle = adj->value * M_PI/180;
2406         persp->set_infinite_direction (axis, NR::Point (cos (angle), sin (angle)));
2408         // FIXME: See comment above; without the if construct we get segfaults during undo.
2409         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2410             sp_document_maybe_done(sp_desktop_document(desktop), "perspectiveangle", SP_VERB_CONTEXT_3DBOX,
2411                              _("3D Box: Change perspective"));
2412         }
2413     }
2414     //g_object_set_data(G_OBJECT(dataKludge), "freeze", GINT_TO_POINTER(FALSE));
2417 static void sp_3dbox_vpx_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2419     sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::X);
2422 static void sp_3dbox_vpy_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2424     sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::Y);
2427 static void sp_3dbox_vpz_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
2429     sp_3dbox_vp_angle_changed (adj, dataKludge, Box3D::Z);
2432 // normalize angle so that it lies in the interval [0,360]
2433 static double sp_3dbox_normalize_angle (double a) {
2434     double angle = a + ((int) (a/360.0))*360;
2435     if (angle < 0) {
2436         angle += 360.0;
2437     }
2438     return angle;
2441 static void sp_3dbox_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const *name,
2442                                            gchar const */*old_value*/, gchar const */*new_value*/,
2443                                            bool /*is_interactive*/, gpointer data)
2445     GtkWidget *tbl = GTK_WIDGET(data);
2447     // FIXME: if we check for "freeze" as in other tools, no action is performed at all ...
2448     /***
2449     // quit if run by the _changed callbacks
2450     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2451         return;
2452     }
2454     // in turn, prevent callbacks from responding
2455     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2456     ***/
2458     if (!strcmp(name, "inkscape:perspective")) {
2459         GtkAdjustment *adj = 0;
2460         double angle;
2461         SPDesktop *desktop = (SPDesktop *) g_object_get_data(G_OBJECT(tbl), "desktop");
2462         Box3D::Perspective3D *persp = sp_desktop_document (desktop)->current_perspective;
2464         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_x"));
2465         angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::X)->get_angle());
2466         gtk_adjustment_set_value(adj, angle);
2468         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_y"));
2469         angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::Y)->get_angle());
2470         gtk_adjustment_set_value(adj, angle);
2472         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_z"));
2473         angle = sp_3dbox_normalize_angle (persp->get_vanishing_point (Box3D::Z)->get_angle());
2474         gtk_adjustment_set_value(adj, angle);
2475     }
2478 static Inkscape::XML::NodeEventVector sp_3dbox_tb_repr_events =
2480     NULL, /* child_added */
2481     NULL, /* child_removed */
2482     sp_3dbox_tb_event_attr_changed,
2483     NULL, /* content_changed */
2484     NULL  /* order_changed */
2485 };
2487 /**
2488  *  \param selection Should not be NULL.
2489  */
2490 static void
2491 sp_3dbox_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2493     Inkscape::XML::Node *repr = NULL;
2494     purge_repr_listener(tbl, tbl);
2496     SPItem *item = selection->singleItem();
2497     if (item) {
2498         repr = SP_OBJECT_REPR(item);
2499         if (repr) {
2500             g_object_set_data(tbl, "repr", repr);
2501             Inkscape::GC::anchor(repr);
2502             sp_repr_add_listener(repr, &sp_3dbox_tb_repr_events, tbl);
2503             sp_repr_synthesize_events(repr, &sp_3dbox_tb_repr_events, tbl);
2504         }
2505     }
2508 static void sp_3dbox_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2510     EgeAdjustmentAction* eact = 0;
2511     SPDocument *document = sp_desktop_document (desktop);
2512     Box3D::Perspective3D *persp = document->current_perspective;
2513     bool toggled = false;
2515     /* angle of VP in X direction */
2516     eact = create_adjustment_action("3DBoxPosAngleXAction",
2517                                     _("Angle X"), _("Angle X:"), _("Angle of infinite vanishing point in X direction"),
2518                                     "tools.shapes.3dbox", "dir_vp_x", persp->get_vanishing_point (Box3D::X)->get_angle(),
2519                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2520                                     0.0, 360.0, 1.0, 10.0,
2521                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2522                                     sp_3dbox_vpx_angle_changed,
2523                                     0.1, 1);
2524     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2525     g_object_set_data(holder, "box3d_angle_x_action", eact);
2526     if (!persp->get_vanishing_point (Box3D::X)->is_finite()) {
2527         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2528     } else {
2529         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2530     }
2532     /* toggle VP in X direction */
2533     {
2534     InkToggleAction* act = ink_toggle_action_new("3DBoxVPXAction",
2535                                                   _("Toggle VP in X direction"),
2536                                                   _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2537                                                   "toggle_vp_x",
2538                                                   Inkscape::ICON_SIZE_DECORATION);
2539     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2540     if (persp) {
2541         toggled = !persp->get_vanishing_point(Box3D::X)->is_finite();
2542     }
2543     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
2544     /* we connect the signal after setting the state to avoid switching the state again */
2545     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_x_changed), holder);
2546     }
2548     /* angle of VP in Y direction */
2549     eact = create_adjustment_action("3DBoxPosAngleYAction",
2550                                     _("Angle Y"), _("Angle Y:"), _("Angle of infinite vanishing point in Y direction"),
2551                                     "tools.shapes.3dbox", "dir_vp_y", persp->get_vanishing_point (Box3D::Y)->get_angle(),
2552                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2553                                     0.0, 360.0, 1.0, 10.0,
2554                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2555                                     sp_3dbox_vpy_angle_changed,
2556                                     0.1, 1);
2557     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2558     g_object_set_data(holder, "box3d_angle_y_action", eact);
2559     if (!persp->get_vanishing_point (Box3D::Y)->is_finite()) {
2560         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2561     } else {
2562         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2563     }
2565     /* toggle VP in Y direction */
2566     {
2567     InkToggleAction* act = ink_toggle_action_new("3DBoxVPYAction",
2568                                                  _("Toggle VP in Y direction"),
2569                                                  _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2570                                                  "toggle_vp_y",
2571                                                  Inkscape::ICON_SIZE_DECORATION);
2572     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2573     if (persp) {
2574         toggled = !persp->get_vanishing_point(Box3D::Y)->is_finite();
2575     }
2576     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
2577     /* we connect the signal after setting the state to avoid switching the state again */
2578     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_y_changed), holder);
2579     }
2581     /* angle of VP in Z direction */
2582     eact = create_adjustment_action("3DBoxPosAngleZAction",
2583                                     _("Angle Z"), _("Angle Z:"), _("Angle of infinite vanishing point in Z direction"),
2584                                     "tools.shapes.3dbox", "dir_vp_z", persp->get_vanishing_point (Box3D::Z)->get_angle(),
2585                                     GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2586                                      0.0, 360.0, 1.0, 10.0,
2587                                     0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
2588                                     sp_3dbox_vpz_angle_changed,
2589                                     0.1, 1);
2591     gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
2592     g_object_set_data(holder, "box3d_angle_z_action", eact);
2593     if (!persp->get_vanishing_point (Box3D::Z)->is_finite()) {
2594         gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
2595     } else {
2596         gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
2597     }
2599     /* toggle VP in Z direction */
2600     {
2601     InkToggleAction* act = ink_toggle_action_new("3DBoxVPZAction",
2602                                                  _("Toggle VP in Z direction"),
2603                                                  _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2604                                                  "toggle_vp_z",
2605                                                  Inkscape::ICON_SIZE_DECORATION);
2606     gtk_action_group_add_action(mainActions, GTK_ACTION(act));
2607     if (persp) {
2608         toggled = !persp->get_vanishing_point(Box3D::Z)->is_finite();
2609     }
2610     /* we connect the signal after setting the state to avoid switching the state again */
2611     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), toggled);
2612     g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_3dbox_toggle_vp_z_changed), holder);
2613     }
2615     sigc::connection *connection = new sigc::connection(
2616         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_3dbox_toolbox_selection_changed), (GObject *)holder))
2617        );
2618     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2619     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2622 //########################
2623 //##       Spiral       ##
2624 //########################
2626 static void
2627 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2629     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2631     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2632         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2633     }
2635     // quit if run by the attr_changed listener
2636     if (g_object_get_data( tbl, "freeze" )) {
2637         return;
2638     }
2640     // in turn, prevent listener from responding
2641     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2643     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2645     bool modmade = false;
2646     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2647          items != NULL;
2648          items = items->next)
2649     {
2650         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2651             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2652             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2653             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2654             modmade = true;
2655         }
2656     }
2658     g_free(namespaced_name);
2660     if (modmade) {
2661         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2662                                    _("Change spiral"));
2663     }
2665     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2668 static void
2669 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2671     sp_spl_tb_value_changed(adj, tbl, "revolution");
2674 static void
2675 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2677     sp_spl_tb_value_changed(adj, tbl, "expansion");
2680 static void
2681 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2683     sp_spl_tb_value_changed(adj, tbl, "t0");
2686 static void
2687 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2689     GtkWidget *tbl = GTK_WIDGET(obj);
2691     GtkAdjustment *adj;
2693     // fixme: make settable
2694     gdouble rev = 5;
2695     gdouble exp = 1.0;
2696     gdouble t0 = 0.0;
2698     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2699     gtk_adjustment_set_value(adj, rev);
2700     gtk_adjustment_value_changed(adj);
2702     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2703     gtk_adjustment_set_value(adj, exp);
2704     gtk_adjustment_value_changed(adj);
2706     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2707     gtk_adjustment_set_value(adj, t0);
2708     gtk_adjustment_value_changed(adj);
2710     spinbutton_defocus(GTK_OBJECT(tbl));
2714 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2715                                          gchar const */*old_value*/, gchar const */*new_value*/,
2716                                          bool /*is_interactive*/, gpointer data)
2718     GtkWidget *tbl = GTK_WIDGET(data);
2720     // quit if run by the _changed callbacks
2721     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2722         return;
2723     }
2725     // in turn, prevent callbacks from responding
2726     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2728     GtkAdjustment *adj;
2729     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2730     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2732     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2733     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2735     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2736     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2738     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2742 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2743     NULL, /* child_added */
2744     NULL, /* child_removed */
2745     spiral_tb_event_attr_changed,
2746     NULL, /* content_changed */
2747     NULL  /* order_changed */
2748 };
2750 static void
2751 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2753     int n_selected = 0;
2754     Inkscape::XML::Node *repr = NULL;
2756     purge_repr_listener( tbl, tbl );
2758     for (GSList const *items = selection->itemList();
2759          items != NULL;
2760          items = items->next)
2761     {
2762         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2763             n_selected++;
2764             repr = SP_OBJECT_REPR((SPItem *) items->data);
2765         }
2766     }
2768     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2770     if (n_selected == 0) {
2771         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2772     } else if (n_selected == 1) {
2773         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2775         if (repr) {
2776             g_object_set_data( tbl, "repr", repr );
2777             Inkscape::GC::anchor(repr);
2778             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2779             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2780         }
2781     } else {
2782         // FIXME: implement averaging of all parameters for multiple selected
2783         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2784         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2785     }
2789 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2791     EgeAdjustmentAction* eact = 0;
2793     {
2794         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2795         ege_output_action_set_use_markup( act, TRUE );
2796         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2797         g_object_set_data( holder, "mode_action", act );
2798     }
2800     /* Revolution */
2801     {
2802         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2803         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2804         eact = create_adjustment_action( "SpiralRevolutionAction",
2805                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
2806                                          "tools.shapes.spiral", "revolution", 3.0,
2807                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2808                                          0.01, 1024.0, 0.1, 1.0,
2809                                          labels, values, G_N_ELEMENTS(labels),
2810                                          sp_spl_tb_revolution_value_changed, 1, 2);
2811         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2812     }
2814     /* Expansion */
2815     {
2816         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2817         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2818         eact = create_adjustment_action( "SpiralExpansionAction",
2819                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2820                                          "tools.shapes.spiral", "expansion", 1.0,
2821                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2822                                          0.0, 1000.0, 0.01, 1.0,
2823                                          labels, values, G_N_ELEMENTS(labels),
2824                                          sp_spl_tb_expansion_value_changed);
2825         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2826     }
2828     /* T0 */
2829     {
2830         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2831         gdouble values[] = {0, 0.5, 0.9};
2832         eact = create_adjustment_action( "SpiralT0Action",
2833                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2834                                          "tools.shapes.spiral", "t0", 0.0,
2835                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2836                                          0.0, 0.999, 0.01, 1.0,
2837                                          labels, values, G_N_ELEMENTS(labels),
2838                                          sp_spl_tb_t0_value_changed);
2839         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2840     }
2842     /* Reset */
2843     {
2844         InkAction* inky = ink_action_new( "SpiralResetAction",
2845                                           _("Defaults"),
2846                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2847                                           GTK_STOCK_CLEAR,
2848                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2849         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2850         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2851     }
2854     sigc::connection *connection = new sigc::connection(
2855         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2856         );
2857     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2858     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2861 //########################
2862 //##     Pen/Pencil    ##
2863 //########################
2866 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2868     // Put stuff here
2871 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2873     // Put stuff here
2876 //########################
2877 //##       Tweak        ##
2878 //########################
2880 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2882     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2885 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2887     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2890 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
2892     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2895 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2897     int mode = ege_select_one_action_get_active( act );
2898     prefs_set_int_attribute("tools.tweak", "mode", mode);
2900     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2901     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2902     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2903     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2904     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2905     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2906     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
2907         if (doh) gtk_action_set_sensitive (doh, TRUE);
2908         if (dos) gtk_action_set_sensitive (dos, TRUE);
2909         if (dol) gtk_action_set_sensitive (dol, TRUE);
2910         if (doo) gtk_action_set_sensitive (doo, TRUE);
2911         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
2912         if (fid) gtk_action_set_sensitive (fid, FALSE);
2913     } else {
2914         if (doh) gtk_action_set_sensitive (doh, FALSE);
2915         if (dos) gtk_action_set_sensitive (dos, FALSE);
2916         if (dol) gtk_action_set_sensitive (dol, FALSE);
2917         if (doo) gtk_action_set_sensitive (doo, FALSE);
2918         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
2919         if (fid) gtk_action_set_sensitive (fid, TRUE);
2920     }
2923 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2925     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
2928 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
2929     bool show = gtk_toggle_action_get_active( act );
2930     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
2932 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
2933     bool show = gtk_toggle_action_get_active( act );
2934     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
2936 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
2937     bool show = gtk_toggle_action_get_active( act );
2938     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
2940 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
2941     bool show = gtk_toggle_action_get_active( act );
2942     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
2945 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2947     {
2948         /* Width */
2949         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
2950         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
2951         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
2952                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
2953                                                               "tools.tweak", "width", 15,
2954                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
2955                                                               1, 100, 1.0, 10.0,
2956                                                               labels, values, G_N_ELEMENTS(labels),
2957                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
2958         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2959         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2960     }
2963     {
2964         /* Force */
2965         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
2966         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
2967         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
2968                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
2969                                                               "tools.tweak", "force", 20,
2970                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
2971                                                               1, 100, 1.0, 10.0,
2972                                                               labels, values, G_N_ELEMENTS(labels),
2973                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
2974         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2975         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2976     }
2978     /* Mode */
2979     {
2980         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2982         GtkTreeIter iter;
2983         gtk_list_store_append( model, &iter );
2984         gtk_list_store_set( model, &iter,
2985                             0, _("Push mode"),
2986                             1, _("Push parts of paths in any direction"),
2987                             2, "tweak_push_mode",
2988                             -1 );
2990         gtk_list_store_append( model, &iter );
2991         gtk_list_store_set( model, &iter,
2992                             0, _("Shrink mode"),
2993                             1, _("Shrink (inset) parts of paths"),
2994                             2, "tweak_shrink_mode",
2995                             -1 );
2997         gtk_list_store_append( model, &iter );
2998         gtk_list_store_set( model, &iter,
2999                             0, _("Grow mode"),
3000                             1, _("Grow (outset) parts of paths"),
3001                             2, "tweak_grow_mode",
3002                             -1 );
3004         gtk_list_store_append( model, &iter );
3005         gtk_list_store_set( model, &iter,
3006                             0, _("Attract mode"),
3007                             1, _("Attract parts of paths towards cursor"),
3008                             2, "tweak_attract_mode",
3009                             -1 );
3011         gtk_list_store_append( model, &iter );
3012         gtk_list_store_set( model, &iter,
3013                             0, _("Repel mode"),
3014                             1, _("Repel parts of paths from cursor"),
3015                             2, "tweak_repel_mode",
3016                             -1 );
3018         gtk_list_store_append( model, &iter );
3019         gtk_list_store_set( model, &iter,
3020                             0, _("Roughen mode"),
3021                             1, _("Roughen parts of paths"),
3022                             2, "tweak_roughen_mode",
3023                             -1 );
3025         gtk_list_store_append( model, &iter );
3026         gtk_list_store_set( model, &iter,
3027                             0, _("Color paint mode"),
3028                             1, _("Paint the tool's color upon selected objects"),
3029                             2, "tweak_colorpaint_mode",
3030                             -1 );
3032         gtk_list_store_append( model, &iter );
3033         gtk_list_store_set( model, &iter,
3034                             0, _("Color jitter mode"),
3035                             1, _("Jitter the colors of selected objects"),
3036                             2, "tweak_colorjitter_mode",
3037                             -1 );
3039         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3040         g_object_set( act, "short_label", _("Mode:"), NULL );
3041         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3042         g_object_set_data( holder, "mode_action", act );
3044         ege_select_one_action_set_appearance( act, "full" );
3045         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3046         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3047         ege_select_one_action_set_icon_column( act, 2 );
3048         ege_select_one_action_set_tooltip_column( act, 1  );
3050         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3051         ege_select_one_action_set_active( act, mode );
3052         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3054         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3055     }
3057     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3059     {
3060         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3061         ege_output_action_set_use_markup( act, TRUE );
3062         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3063         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3064             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3065         g_object_set_data( holder, "tweak_channels_label", act);
3066     }
3068     {
3069         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3070                                                       _("Hue"),
3071                                                       _("In color mode, act on objects' hue"),
3072                                                       NULL,
3073                                                       Inkscape::ICON_SIZE_DECORATION );
3074         g_object_set( act, "short_label", _("H"), NULL );
3075         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3076         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3077         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3078         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3079             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3080         g_object_set_data( holder, "tweak_doh", act);
3081     }
3082     {
3083         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3084                                                       _("Saturation"),
3085                                                       _("In color mode, act on objects' saturation"),
3086                                                       NULL,
3087                                                       Inkscape::ICON_SIZE_DECORATION );
3088         g_object_set( act, "short_label", _("S"), NULL );
3089         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3090         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3091         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3092         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3093             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3094         g_object_set_data( holder, "tweak_dos", act );
3095     }
3096     {
3097         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3098                                                       _("Lightness"),
3099                                                       _("In color mode, act on objects' lightness"),
3100                                                       NULL,
3101                                                       Inkscape::ICON_SIZE_DECORATION );
3102         g_object_set( act, "short_label", _("L"), NULL );
3103         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3104         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3105         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3106         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3107             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3108         g_object_set_data( holder, "tweak_dol", act );
3109     }
3110     {
3111         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3112                                                       _("Opacity"),
3113                                                       _("In color mode, act on objects' opacity"),
3114                                                       NULL,
3115                                                       Inkscape::ICON_SIZE_DECORATION );
3116         g_object_set( act, "short_label", _("O"), NULL );
3117         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3118         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3119         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
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_doo", act );
3123     }
3125     {   /* Fidelity */
3126         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3127         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3128         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3129                                                               _("Fidelity"), _("Fidelity:"),
3130                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3131                                                               "tools.tweak", "fidelity", 50,
3132                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3133                                                               1, 100, 1.0, 10.0,
3134                                                               labels, values, G_N_ELEMENTS(labels),
3135                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3136         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3137         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3138         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3139             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3140         g_object_set_data( holder, "tweak_fidelity", eact );
3141     }
3144     /* Use Pressure button */
3145     {
3146         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3147                                                       _("Pressure"),
3148                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3149                                                       "use_pressure",
3150                                                       Inkscape::ICON_SIZE_DECORATION );
3151         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3152         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3153         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3154     }
3159 //########################
3160 //##     Calligraphy    ##
3161 //########################
3163 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3165     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3168 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3170     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3173 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3175     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3178 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3180     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3183 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3185     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3188 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3190     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3193 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3195     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3198 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3200     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3203 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3205     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3208 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3210     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3213 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3215     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3217     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3220 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3222     // FIXME: make defaults settable via Inkscape Options
3223     struct KeyValue {
3224         char const *key;
3225         double value;
3226     } const key_values[] = {
3227         {"mass", 0.02},
3228         {"wiggle", 0.0},
3229         {"angle", 30.0},
3230         {"width", 15},
3231         {"thinning", 0.1},
3232         {"tremor", 0.0},
3233         {"flatness", 0.9},
3234         {"cap_rounding", 0.0}
3235     };
3237     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3238         KeyValue const &kv = key_values[i];
3239         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3240         if ( adj ) {
3241             gtk_adjustment_set_value(adj, kv.value);
3242         }
3243     }
3247 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3249     {
3250         EgeAdjustmentAction* calligraphy_angle = 0;
3252         {
3253         /* Width */
3254         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3255         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3256         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3257                                                               _("Pen Width"), _("Width:"),
3258                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3259                                                               "tools.calligraphic", "width", 15,
3260                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3261                                                               1, 100, 1.0, 10.0,
3262                                                               labels, values, G_N_ELEMENTS(labels),
3263                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3264         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3265         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3266         }
3268         {
3269         /* Thinning */
3270             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3271             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3272         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3273                                                               _("Stroke Thinning"), _("Thinning:"),
3274                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3275                                                               "tools.calligraphic", "thinning", 0.1,
3276                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3277                                                               -1.0, 1.0, 0.01, 0.1,
3278                                                               labels, values, G_N_ELEMENTS(labels),
3279                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3280         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3281         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3282         }
3284         {
3285         /* Angle */
3286         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3287         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3288         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3289                                                               _("Pen Angle"), _("Angle:"),
3290                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3291                                                               "tools.calligraphic", "angle", 30,
3292                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3293                                                               -90.0, 90.0, 1.0, 10.0,
3294                                                               labels, values, G_N_ELEMENTS(labels),
3295                                                               sp_ddc_angle_value_changed, 1, 0 );
3296         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3297         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3298         calligraphy_angle = eact;
3299         }
3301         {
3302         /* Fixation */
3303             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3304         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3305         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3306                                                               _("Fixation"), _("Fixation:"),
3307                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3308                                                               "tools.calligraphic", "flatness", 0.9,
3309                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3310                                                               0.0, 1.0, 0.01, 0.1,
3311                                                               labels, values, G_N_ELEMENTS(labels),
3312                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3313         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3314         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3315         }
3317         {
3318         /* Cap Rounding */
3319             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3320         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3321         // TRANSLATORS: "cap" means "end" (both start and finish) here
3322         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3323                                                               _("Cap rounding"), _("Caps:"),
3324                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3325                                                               "tools.calligraphic", "cap_rounding", 0.0,
3326                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3327                                                               0.0, 5.0, 0.01, 0.1,
3328                                                               labels, values, G_N_ELEMENTS(labels),
3329                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3330         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3331         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3332         }
3334         {
3335         /* Tremor */
3336             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3337         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3338         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3339                                                               _("Stroke Tremor"), _("Tremor:"),
3340                                                               _("Increase to make strokes rugged and trembling"),
3341                                                               "tools.calligraphic", "tremor", 0.0,
3342                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3343                                                               0.0, 1.0, 0.01, 0.1,
3344                                                               labels, values, G_N_ELEMENTS(labels),
3345                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3347         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3348         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3349         }
3351         {
3352         /* Wiggle */
3353         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3354         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3355         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3356                                                               _("Pen Wiggle"), _("Wiggle:"),
3357                                                               _("Increase to make the pen waver and wiggle"),
3358                                                               "tools.calligraphic", "wiggle", 0.0,
3359                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3360                                                               0.0, 1.0, 0.01, 0.1,
3361                                                               labels, values, G_N_ELEMENTS(labels),
3362                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3363         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3364         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3365         }
3367         {
3368         /* Mass */
3369             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3370         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3371         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3372                                                               _("Pen Mass"), _("Mass:"),
3373                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3374                                                               "tools.calligraphic", "mass", 0.02,
3375                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3376                                                               0.0, 1.0, 0.01, 0.1,
3377                                                               labels, values, G_N_ELEMENTS(labels),
3378                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3379         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3380         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3381         }
3384         /* Trace Background button */
3385         {
3386             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3387                                                           _("Trace Background"),
3388                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3389                                                           "trace_background",
3390                                                           Inkscape::ICON_SIZE_DECORATION );
3391             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3392             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3393             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3394         }
3396         /* Use Pressure button */
3397         {
3398             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3399                                                           _("Pressure"),
3400                                                           _("Use the pressure of the input device to alter the width of the pen"),
3401                                                           "use_pressure",
3402                                                           Inkscape::ICON_SIZE_DECORATION );
3403             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3404             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3405             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3406         }
3408         /* Use Tilt button */
3409         {
3410             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3411                                                           _("Tilt"),
3412                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3413                                                           "use_tilt",
3414                                                           Inkscape::ICON_SIZE_DECORATION );
3415             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3416             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3417             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3418             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3419         }
3421         /* Reset */
3422         {
3423             GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3424                                              _("Defaults"),
3425                                              _("Reset all parameters to defaults"),
3426                                              GTK_STOCK_CLEAR );
3427             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3428             gtk_action_group_add_action( mainActions, act );
3429             gtk_action_set_sensitive( act, TRUE );
3430         }
3431     }
3435 //########################
3436 //##    Circle / Arc    ##
3437 //########################
3439 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3441     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3442     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3444     if (v1 == 0 && v2 == 0) {
3445         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3446             gtk_action_set_sensitive( ocb, FALSE );
3447             gtk_action_set_sensitive( make_whole, FALSE );
3448         }
3449     } else {
3450         gtk_action_set_sensitive( ocb, TRUE );
3451         gtk_action_set_sensitive( make_whole, TRUE );
3452     }
3455 static void
3456 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3458     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3460     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3461         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3462     }
3464     // quit if run by the attr_changed listener
3465     if (g_object_get_data( tbl, "freeze" )) {
3466         return;
3467     }
3469     // in turn, prevent listener from responding
3470     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3472     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3474     bool modmade = false;
3475     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3476          items != NULL;
3477          items = items->next)
3478     {
3479         SPItem *item = SP_ITEM(items->data);
3481         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3483             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3484             SPArc *arc = SP_ARC(item);
3486             if (!strcmp(value_name, "start"))
3487                 ge->start = (adj->value * M_PI)/ 180;
3488             else
3489                 ge->end = (adj->value * M_PI)/ 180;
3491             sp_genericellipse_normalize(ge);
3492             ((SPObject *)arc)->updateRepr();
3493             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3495             modmade = true;
3496         }
3497     }
3499     g_free(namespaced_name);
3501     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3503     sp_arctb_sensitivize( tbl, adj->value, other->value );
3505     if (modmade) {
3506         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3507                                    _("Arc: Change start/end"));
3508     }
3510     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3514 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3516     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3519 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3521     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3524 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3526     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3527     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3528         if ( ege_select_one_action_get_active( act ) != 0 ) {
3529             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3530         } else {
3531             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3532         }
3533     }
3535     // quit if run by the attr_changed listener
3536     if (g_object_get_data( tbl, "freeze" )) {
3537         return;
3538     }
3540     // in turn, prevent listener from responding
3541     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3543     bool modmade = false;
3545     if ( ege_select_one_action_get_active(act) != 0 ) {
3546         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3547              items != NULL;
3548              items = items->next)
3549         {
3550             if (SP_IS_ARC((SPItem *) items->data)) {
3551                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3552                 repr->setAttribute("sodipodi:open", "true");
3553                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3554                 modmade = true;
3555             }
3556         }
3557     } else {
3558         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3559              items != NULL;
3560              items = items->next)
3561         {
3562             if (SP_IS_ARC((SPItem *) items->data))    {
3563                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3564                 repr->setAttribute("sodipodi:open", NULL);
3565                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3566                 modmade = true;
3567             }
3568         }
3569     }
3571     if (modmade) {
3572         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3573                                    _("Arc: Change open/closed"));
3574     }
3576     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3579 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3581     GtkAdjustment *adj;
3582     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3583     gtk_adjustment_set_value(adj, 0.0);
3584     gtk_adjustment_value_changed(adj);
3586     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3587     gtk_adjustment_set_value(adj, 0.0);
3588     gtk_adjustment_value_changed(adj);
3590     spinbutton_defocus( GTK_OBJECT(obj) );
3593 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3594                                       gchar const */*old_value*/, gchar const */*new_value*/,
3595                                       bool /*is_interactive*/, gpointer data)
3597     GObject *tbl = G_OBJECT(data);
3599     // quit if run by the _changed callbacks
3600     if (g_object_get_data( tbl, "freeze" )) {
3601         return;
3602     }
3604     // in turn, prevent callbacks from responding
3605     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3607     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3608     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3610     GtkAdjustment *adj1,*adj2;
3611     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3612     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3613     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3614     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3616     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3618     char const *openstr = NULL;
3619     openstr = repr->attribute("sodipodi:open");
3620     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3622     if (openstr) {
3623         ege_select_one_action_set_active( ocb, 1 );
3624     } else {
3625         ege_select_one_action_set_active( ocb, 0 );
3626     }
3628     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3631 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3632     NULL, /* child_added */
3633     NULL, /* child_removed */
3634     arc_tb_event_attr_changed,
3635     NULL, /* content_changed */
3636     NULL  /* order_changed */
3637 };
3640 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3642     int n_selected = 0;
3643     Inkscape::XML::Node *repr = NULL;
3645     purge_repr_listener( tbl, tbl );
3647     for (GSList const *items = selection->itemList();
3648          items != NULL;
3649          items = items->next)
3650     {
3651         if (SP_IS_ARC((SPItem *) items->data)) {
3652             n_selected++;
3653             repr = SP_OBJECT_REPR((SPItem *) items->data);
3654         }
3655     }
3657     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3659     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3660     if (n_selected == 0) {
3661         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3662     } else if (n_selected == 1) {
3663         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3664         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3666         if (repr) {
3667             g_object_set_data( tbl, "repr", repr );
3668             Inkscape::GC::anchor(repr);
3669             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3670             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3671         }
3672     } else {
3673         // FIXME: implement averaging of all parameters for multiple selected
3674         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3675         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3676         sp_arctb_sensitivize( tbl, 1, 0 );
3677     }
3681 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3683     EgeAdjustmentAction* eact = 0;
3686     {
3687         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3688         ege_output_action_set_use_markup( act, TRUE );
3689         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3690         g_object_set_data( holder, "mode_action", act );
3691     }
3693     /* Start */
3694     {
3695         eact = create_adjustment_action( "ArcStartAction",
3696                                          _("Start"), _("Start:"),
3697                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
3698                                          "tools.shapes.arc", "start", 0.0,
3699                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3700                                          -360.0, 360.0, 1.0, 10.0,
3701                                          0, 0, 0,
3702                                          sp_arctb_start_value_changed);
3703         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3704     }
3706     /* End */
3707     {
3708         eact = create_adjustment_action( "ArcEndAction",
3709                                          _("End"), _("End:"),
3710                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
3711                                          "tools.shapes.arc", "end", 0.0,
3712                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3713                                          -360.0, 360.0, 1.0, 10.0,
3714                                          0, 0, 0,
3715                                          sp_arctb_end_value_changed);
3716         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3717     }
3719     /* Segments / Pie checkbox */
3720     {
3721         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3723         GtkTreeIter iter;
3724         gtk_list_store_append( model, &iter );
3725         gtk_list_store_set( model, &iter,
3726                             0, _("Closed arc"),
3727                             1, _("Switch to segment (closed shape with two radii)"),
3728                             2, "circle_closed_arc",
3729                             -1 );
3731         gtk_list_store_append( model, &iter );
3732         gtk_list_store_set( model, &iter,
3733                             0, _("Open Arc"),
3734                             1, _("Switch to arc (unclosed shape)"),
3735                             2, "circle_open_arc",
3736                             -1 );
3738         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3739         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3740         g_object_set_data( holder, "open_action", act );
3742         ege_select_one_action_set_appearance( act, "full" );
3743         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3744         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3745         ege_select_one_action_set_icon_column( act, 2 );
3746         ege_select_one_action_set_tooltip_column( act, 1  );
3748         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3749         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3750         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3751         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3752     }
3754     /* Make Whole */
3755     {
3756         InkAction* inky = ink_action_new( "ArcResetAction",
3757                                           _("Make whole"),
3758                                           _("Make the shape a whole ellipse, not arc or segment"),
3759                                           "reset_circle",
3760                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3761         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3762         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3763         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3764         g_object_set_data( holder, "make_whole", inky );
3765     }
3767     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3768     // sensitivize make whole and open checkbox
3769     {
3770         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3771         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3772         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3773     }
3776     sigc::connection *connection = new sigc::connection(
3777         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3778         );
3779     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3780     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3786 // toggle button callbacks and updaters
3788 //########################
3789 //##      Dropper       ##
3790 //########################
3792 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3793     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3794     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3795     if ( set_action ) {
3796         if ( gtk_toggle_action_get_active( act ) ) {
3797             gtk_action_set_sensitive( set_action, TRUE );
3798         } else {
3799             gtk_action_set_sensitive( set_action, FALSE );
3800         }
3801     }
3803     spinbutton_defocus(GTK_OBJECT(tbl));
3806 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3807     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3808     spinbutton_defocus(GTK_OBJECT(tbl));
3812 /**
3813  * Dropper auxiliary toolbar construction and setup.
3814  *
3815  * TODO: Would like to add swatch of current color.
3816  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3817  *       can drag and drop places. Will provide a nice mixing palette.
3818  */
3819 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3821     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3823     {
3824         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3825                                                       _("Pick alpha"),
3826                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3827                                                       "color_alpha_get",
3828                                                       Inkscape::ICON_SIZE_DECORATION );
3829         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3830         g_object_set_data( holder, "pick_action", act );
3831         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3832         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3833     }
3835     {
3836         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3837                                                       _("Set alpha"),
3838                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3839                                                       "color_alpha_set",
3840                                                       Inkscape::ICON_SIZE_DECORATION );
3841         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3842         g_object_set_data( holder, "set_action", act );
3843         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3844         // make sure it's disabled if we're not picking alpha
3845         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3846         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3847     }
3851 //########################
3852 //##    Text Toolbox    ##
3853 //########################
3854 /*
3855 static void
3856 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3858     //Call back for letter sizing spinbutton
3861 static void
3862 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3864     //Call back for line height spinbutton
3867 static void
3868 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3870     //Call back for horizontal kerning spinbutton
3873 static void
3874 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3876     //Call back for vertical kerning spinbutton
3879 static void
3880 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3882     //Call back for letter rotation spinbutton
3883 }*/
3885 namespace {
3887 bool visible = false;
3889 void
3890 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
3892     SPStyle *query =
3893         sp_style_new (SP_ACTIVE_DOCUMENT);
3895     int result_family =
3896         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3898     int result_style =
3899         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3901     int result_numbers =
3902         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3904     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3906     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3907     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3908     {
3909         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3911         if (repr)
3912         {
3913             sp_style_read_from_repr (query, repr);
3914         }
3915         else
3916         {
3917             return;
3918         }
3919     }
3921     if (query->text)
3922     {
3923         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
3924             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3925             gtk_entry_set_text (GTK_ENTRY (entry), "");
3927         } else if (query->text->font_family.value) {
3929             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3930             gtk_entry_set_text (GTK_ENTRY (entry), query->text->font_family.value);
3932             Gtk::TreePath path;
3933             try {
3934                 path = Inkscape::FontLister::get_instance()->get_row_for_font (query->text->font_family.value);
3935             } catch (...) {
3936                 return;
3937             }
3939             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
3940             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
3942             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
3944             gtk_tree_selection_select_path (tselection, path.gobj());
3945             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
3947             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
3948         }
3950         //Size
3951         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
3952         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
3953         g_object_set_data (tbl, "size-block", gpointer(1));
3954         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
3955         g_object_set_data (tbl, "size-block", gpointer(0));
3956         free (str);
3958         //Anchor
3959         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
3960         {
3961             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
3962             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3963             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3964             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3965         }
3966         else
3967         {
3968             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
3969             {
3970                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
3971                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3972                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3973                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3974             }
3975             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
3976             {
3977                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
3978                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3979                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3980                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3981             }
3982             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
3983             {
3984                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
3985                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
3986                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
3987                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
3988             }
3989         }
3991         //Style
3992         {
3993             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
3995             gboolean active = gtk_toggle_button_get_active (button);
3996             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
3998             if (active != check)
3999             {
4000                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4001                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4002                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4003             }
4004         }
4006         {
4007             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4009             gboolean active = gtk_toggle_button_get_active (button);
4010             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4012             if (active != check)
4013             {
4014                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4015                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4016                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4017             }
4018         }
4020         //Orientation
4021         //locking both buttons, changing one affect all group (both)
4022         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4023         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4025         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4026         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4028         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4029         {
4030             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4031         }
4032         else
4033         {
4034             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4035         }
4036         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4037         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4038     }
4040     sp_style_unref(query);
4043 void
4044 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4046     sp_text_toolbox_selection_changed (selection, tbl);
4049 void
4050 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4052     sp_text_toolbox_selection_changed (NULL, tbl);
4055 void
4056 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4057                                 GObject             *tbl)
4059     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4060     GtkTreeModel *model = 0;
4061     GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4062     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4063     GtkTreeIter   iter;
4064     char         *family = 0;
4066     (void)popdown;
4068     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4069     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4071     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4072         return;
4073     }
4075     gtk_tree_model_get (model, &iter, 0, &family, -1);
4077     if (g_object_get_data (G_OBJECT (selection), "block"))
4078     {
4079         gtk_entry_set_text (GTK_ENTRY (entry), family);
4080         return;
4081     }
4083     gtk_entry_set_text (GTK_ENTRY (entry), family);
4085     SPStyle *query =
4086         sp_style_new (SP_ACTIVE_DOCUMENT);
4088     int result_numbers =
4089         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4091     SPCSSAttr *css = sp_repr_css_attr_new ();
4092     sp_repr_css_set_property (css, "font-family", family);
4094     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4095     if (result_numbers == QUERY_STYLE_NOTHING)
4096     {
4097         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4098         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4099     }
4100     else
4101     {
4102         sp_desktop_set_style (desktop, css, true, true);
4103     }
4105     sp_style_unref(query);
4107     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4108                                    _("Text: Change font family"));
4109     sp_repr_css_attr_unref (css);
4110     free (family);
4111     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4113     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4116 void
4117 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4118                                        GObject      *tbl)
4120     const char *family = gtk_entry_get_text (entry);
4122     try {
4123         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4124         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4125         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4126         gtk_tree_selection_select_path (selection, path.gobj());
4127         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4128         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4129     } catch (...) {
4130         if (family && strlen (family))
4131         {
4132             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4133         }
4134     }
4137 void
4138 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4139                                    gpointer          data)
4141     if (g_object_get_data (G_OBJECT (button), "block")) return;
4142     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4143     int prop = GPOINTER_TO_INT(data);
4145     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4146     SPCSSAttr *css = sp_repr_css_attr_new ();
4148     switch (prop)
4149     {
4150         case 0:
4151         {
4152             sp_repr_css_set_property (css, "text-anchor", "start");
4153             sp_repr_css_set_property (css, "text-align", "start");
4154             break;
4155         }
4156         case 1:
4157         {
4158             sp_repr_css_set_property (css, "text-anchor", "middle");
4159             sp_repr_css_set_property (css, "text-align", "center");
4160             break;
4161         }
4163         case 2:
4164         {
4165             sp_repr_css_set_property (css, "text-anchor", "end");
4166             sp_repr_css_set_property (css, "text-align", "end");
4167             break;
4168         }
4170         case 3:
4171         {
4172             sp_repr_css_set_property (css, "text-anchor", "start");
4173             sp_repr_css_set_property (css, "text-align", "justify");
4174             break;
4175         }
4176     }
4178     SPStyle *query =
4179         sp_style_new (SP_ACTIVE_DOCUMENT);
4180     int result_numbers =
4181         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4183     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4184     if (result_numbers == QUERY_STYLE_NOTHING)
4185     {
4186         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4187     }
4189     sp_style_unref(query);
4191     sp_desktop_set_style (desktop, css, true, true);
4192     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4193                                    _("Text: Change alignment"));
4194     sp_repr_css_attr_unref (css);
4196     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4199 void
4200 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4201                                gpointer          data)
4203     if (g_object_get_data (G_OBJECT (button), "block")) return;
4205     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4206     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4207     int          prop       = GPOINTER_TO_INT(data);
4208     bool         active     = gtk_toggle_button_get_active (button);
4211     switch (prop)
4212     {
4213         case 0:
4214         {
4215             sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4216             break;
4217         }
4219         case 1:
4220         {
4221             sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4222             break;
4223         }
4224     }
4226     SPStyle *query =
4227         sp_style_new (SP_ACTIVE_DOCUMENT);
4228     int result_numbers =
4229         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4231     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4232     if (result_numbers == QUERY_STYLE_NOTHING)
4233     {
4234         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4235     }
4237     sp_style_unref(query);
4239     sp_desktop_set_style (desktop, css, true, true);
4240     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4241                                    _("Text: Change font style"));
4242     sp_repr_css_attr_unref (css);
4244     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4247 void
4248 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4249                                      gpointer         data)
4251     if (g_object_get_data (G_OBJECT (button), "block")) {
4252         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4253         return;
4254     }
4256     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4257     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4258     int          prop       = GPOINTER_TO_INT(data);
4260     switch (prop)
4261     {
4262         case 0:
4263         {
4264             sp_repr_css_set_property (css, "writing-mode", "lr");
4265             break;
4266         }
4268         case 1:
4269         {
4270             sp_repr_css_set_property (css, "writing-mode", "tb");
4271             break;
4272         }
4273     }
4275     SPStyle *query =
4276         sp_style_new (SP_ACTIVE_DOCUMENT);
4277     int result_numbers =
4278         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4280     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4281     if (result_numbers == QUERY_STYLE_NOTHING)
4282     {
4283         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4284     }
4286     sp_desktop_set_style (desktop, css, true, true);
4287     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4288                                    _("Text: Change orientation"));
4289     sp_repr_css_attr_unref (css);
4291     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4294 gboolean
4295 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, gpointer /*data*/)
4297     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4298     if (!desktop) return FALSE;
4300     switch (get_group0_keyval (event)) {
4301         case GDK_Escape: // defocus
4302             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4303             return TRUE; // I consumed the event
4304             break;
4305         case GDK_Return: // defocus
4306         case GDK_KP_Enter:
4307             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4308             return TRUE; // I consumed the event
4309             break;
4310     }
4311     return FALSE;
4314 gboolean
4315 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4317     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4318     if (!desktop) return FALSE;
4320     switch (get_group0_keyval (event)) {
4321         case GDK_Escape: // defocus
4322             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4323             sp_text_toolbox_selection_changed (NULL, tbl); // update
4324             return TRUE; // I consumed the event
4325             break;
4326     }
4327     return FALSE;
4330 gboolean
4331 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4333     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4334     if (!desktop) return FALSE;
4336     switch (get_group0_keyval (event)) {
4337         case GDK_KP_Enter:
4338         case GDK_Return:
4339         case GDK_Escape: // defocus
4340             gtk_widget_hide (w);
4341             visible = false;
4342             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4343             return TRUE; // I consumed the event
4344             break;
4345     }
4346     return FALSE;
4350 void
4351 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4352                                GObject     *tbl)
4354     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4356     if (g_object_get_data (tbl, "size-block")) return;
4358 #if GTK_CHECK_VERSION(2,6,0)
4359     char *text = gtk_combo_box_get_active_text (cbox);
4360 #else // GTK_CHECK_VERSION(2,6,0)
4361     GtkTreeModel *model = gtk_combo_box_get_model (cbox);
4362     GtkTreeIter iter;
4363     char *text = NULL;
4365     if (gtk_combo_box_get_active_iter (cbox, &iter) && model)
4366         gtk_tree_model_get (model, &iter, 0, &text, -1);
4367 #endif // GTK_CHECK_VERSION(2,6,0)
4369     SPCSSAttr *css = sp_repr_css_attr_new ();
4370     sp_repr_css_set_property (css, "font-size", text);
4371     free (text);
4373     SPStyle *query =
4374         sp_style_new (SP_ACTIVE_DOCUMENT);
4375     int result_numbers =
4376         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4378     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4379     if (result_numbers == QUERY_STYLE_NOTHING)
4380     {
4381         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4382     }
4384     sp_style_unref(query);
4386     sp_desktop_set_style (desktop, css, true, true);
4387     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4388                                    _("Text: Change font size"));
4389     sp_repr_css_attr_unref (css);
4392     if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
4393         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4396 void
4397 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4398                                          GObject            *tbl)
4400     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4401     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4402     int x, y;
4404     if (!visible)
4405     {
4406         gdk_window_get_origin (widget->window, &x, &y);
4407         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4408         gtk_widget_show_all (popdown);
4410         gdk_pointer_grab (widget->window, TRUE,
4411                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4412                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4413                                         GDK_POINTER_MOTION_MASK),
4414                           NULL, NULL, GDK_CURRENT_TIME);
4416         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4418         visible = true;
4419     }
4420     else
4421     {
4422         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4423         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4424         gtk_widget_hide (popdown);
4425         visible = false;
4426     }
4429 gboolean
4430 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4431                                  GdkEventFocus    */*event*/,
4432                                  GObject          */*tbl*/)
4434     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4435     return FALSE;
4438 gboolean
4439 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4440                                    GdkEventFocus    */*event*/,
4441                                    GObject          */*tbl*/)
4443     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4445     gtk_widget_hide (popdown);
4446     visible = false;
4447     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4448     return TRUE;
4451 void
4452 cell_data_func  (GtkTreeViewColumn */*column*/,
4453                  GtkCellRenderer   *cell,
4454                  GtkTreeModel      *tree_model,
4455                  GtkTreeIter       *iter,
4456                  gpointer           /*data*/)
4458     char        *family,
4459         *family_escaped,
4460         *sample_escaped;
4462     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4464     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4466     family_escaped = g_markup_escape_text (family, -1);
4467     sample_escaped = g_markup_escape_text (sample, -1);
4469     std::stringstream markup;
4470     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4471     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4473     free (family);
4474     free (family_escaped);
4475     free (sample_escaped);
4478 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4479     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4480     if (completion) {
4481         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4482         g_object_unref (completion);
4483     }
4486 GtkWidget*
4487 sp_text_toolbox_new (SPDesktop *desktop)
4489     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4491     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4492     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4494     GtkTooltips *tt = gtk_tooltips_new();
4495     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4497     ////////////Family
4498     //Window
4499     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4500     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4502     //Entry
4503     GtkWidget           *entry = gtk_entry_new ();
4504     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4505     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4506     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4507     gtk_entry_completion_set_text_column (completion, 0);
4508     gtk_entry_completion_set_minimum_key_length (completion, 1);
4509     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4510     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4511     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4512     aux_toolbox_space (tbl, 1);
4513     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4514     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4516     //Button
4517     GtkWidget   *button = gtk_button_new ();
4518     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4519     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4521     //Popdown
4522     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4523     GtkWidget           *treeview = gtk_tree_view_new ();
4525     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4526     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4527     gtk_tree_view_column_pack_start (column, cell, FALSE);
4528     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4529     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4530     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4532     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4533     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4534 #if GTK_CHECK_VERSION(2,6,0)
4535     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4536 #endif // GTK_CHECK_VERSION(2,6,0)
4538     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4540     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4541     gtk_container_add (GTK_CONTAINER (sw), treeview);
4543     gtk_container_add (GTK_CONTAINER (window), sw);
4544     gtk_widget_set_size_request (window, 300, 450);
4546     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4547     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4548     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4550     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4552     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4553     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4555     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4556     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4558     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4559     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4560     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4561     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4562     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4564     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4565     aux_toolbox_space (tbl, 1);
4566     GtkWidget *box = gtk_event_box_new ();
4567     gtk_container_add (GTK_CONTAINER (box), image);
4568     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4569     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4570     GtkTooltips *tooltips = gtk_tooltips_new ();
4571     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4572     gtk_widget_hide (GTK_WIDGET (box));
4573     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4575     ////////////Size
4576     const char *sizes[] = {
4577         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4578         "16", "18", "20", "22", "24", "28",
4579         "32", "36", "40", "48", "56", "64", "72", "144"
4580     };
4582     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4583     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4584     gtk_widget_set_size_request (cbox, 80, -1);
4585     aux_toolbox_space (tbl, 1);
4586     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4587     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4588     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4589     gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
4591     //spacer
4592     aux_toolbox_space (tbl, 4);
4593     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4595     ////////////Text anchor
4596     GtkWidget *group   = gtk_radio_button_new (NULL);
4597     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4598     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4600     // left
4601     GtkWidget *rbutton = group;
4602     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4603     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4604     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4606     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4607     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4608     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4609     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4611     // center
4612     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4613     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4614     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4615     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4617     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4618     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
4619     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4620     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4622     // right
4623     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4624     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4625     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4626     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4628     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4629     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
4630     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4631     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4633     // fill
4634     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4635     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4636     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4637     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4639     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4640     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
4641     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4642     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4644     aux_toolbox_space (tbl, 1);
4645     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4647     //spacer
4648     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4650     ////////////Text style
4651     row = gtk_hbox_new (FALSE, 4);
4653     // bold
4654     rbutton = gtk_toggle_button_new ();
4655     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4656     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4657     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4658     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4660     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4661     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
4662     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4664     // italic
4665     rbutton = gtk_toggle_button_new ();
4666     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4667     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4668     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4669     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4671     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4672     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
4673     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4675     aux_toolbox_space (tbl, 1);
4676     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4678     //spacer
4679     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4681     ////////////Text orientation
4682     group   = gtk_radio_button_new (NULL);
4683     row     = gtk_hbox_new (FALSE, 4);
4684     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4686     // horizontal
4687     rbutton = group;
4688     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4689     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4690     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4691     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4693     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4694     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4695     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4697     // vertical
4698     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4699     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4700     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4701     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4702     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4704     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4705     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4706     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4707     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4710     //watch selection
4711     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4713     sigc::connection *c_selection_changed =
4714         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4715                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4716     pool->add_connection ("selection-changed", c_selection_changed);
4718     sigc::connection *c_selection_modified =
4719         new sigc::connection (sp_desktop_selection (desktop)->connectModified
4720                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4721     pool->add_connection ("selection-modified", c_selection_modified);
4723     sigc::connection *c_subselection_changed =
4724         new sigc::connection (desktop->connectToolSubselectionChanged
4725                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4726     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4728     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4731 #if 0
4732     // horizontal
4733     {
4734         GtkWidget *px= sp_icon_new(Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR);
4735         GtkWidget *b = group = gtk_radio_button_new (NULL);
4736         gtk_container_add (GTK_CONTAINER (b), px);
4737         gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
4738         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
4739         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
4740         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
4741     }
4743     // vertical
4744     {
4745         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB);
4746         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4747         gtk_container_add (GTK_CONTAINER (b), px);
4748         gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
4749         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
4750         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
4751         gtk_box_pack_start (GTK_BOX (tbl), b, FALSE, FALSE, 0);
4752     }
4754     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4756     // letter spacing
4757     {
4758         {
4759             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LETTER_SPACING);
4760             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4761             gtk_container_add (GTK_CONTAINER (hb), image);
4762             gtk_widget_show(image);
4763             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4764         }
4766         {
4767             GtkWidget *hb = sp_tb_spinbutton((""), _("Spacing between letters"),
4768                                              "tools.text", "letter_spacing", 0.0,
4769                                              us, tbl, FALSE, NULL,
4770                                              -1000.0, 1000.0, 0.1, 0.1,
4771                                              sp_text_letter_changed, 0.1, 1);
4772             gtk_widget_set_size_request (hb, 45, 6);
4773             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4774         }
4775     }
4777     // line spacing
4778     {
4779         {
4780             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_LINE_SPACING);
4781             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4782             gtk_container_add (GTK_CONTAINER (hb), image);
4783             gtk_widget_show(image);
4784             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4785         }
4787         {
4788             GtkWidget *hb = sp_tb_spinbutton((""), _("Spacing between lines"),
4789                                              "tools.text", "line_spacing", 0,
4790                                              us, tbl, FALSE, NULL,
4791                                              -1000.0, 1000.0, 0.1, 0.1,
4792                                              sp_text_line_changed, 0.1, 1);
4793             gtk_widget_set_size_request (hb, 45, 0);
4794             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 3);
4795         }
4796     }
4798     {
4799         // horizontal kerning/vertical kerning units menu: create
4800         GtkWidget *us = sp_unit_selector_new(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
4801         sp_unit_selector_setsize(us, AUX_OPTION_MENU_WIDTH, AUX_OPTION_MENU_HEIGHT);
4802         sp_unit_selector_set_unit(SP_UNIT_SELECTOR(us), desktop->namedview->doc_units);
4804         aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4806         // horizontal kerning
4807         {
4808             {
4809                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_HORZ_KERN);
4810                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4811                 gtk_container_add (GTK_CONTAINER (hb), image);
4812                 gtk_widget_show(image);
4813                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4814             }
4816             {
4817                 GtkWidget *hb = sp_tb_spinbutton((""), _("Horizontal kerning"),
4818                                                  "tools.text", "horizontal_kerning", 0,
4819                                                  us, tbl, FALSE, NULL,
4820                                                  -100.00, 100.00, 0.01, 0.1,
4821                                                  sp_text_horiz_kern_changed);
4822                 gtk_widget_set_size_request (hb, 45, 0);
4823                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4824             }
4825         }
4827         // vertical kerning
4828         {
4829             {
4830                 GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_VERT_KERN);
4831                 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4832                 gtk_container_add (GTK_CONTAINER (hb), image);
4833                 gtk_widget_show(image);
4834                 gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4835             }
4837             {
4838                 GtkWidget *hb = sp_tb_spinbutton((""), _("Vertical kerning"),
4839                                                  "tools.text", "vertical_kerning", 0,
4840                                                  us, tbl, FALSE, NULL,
4841                                                  -100.00, 100.00, 0.01, 0.1,
4842                                                  sp_text_vert_kern_changed);
4843                 gtk_widget_set_size_request (hb, 45, 0);
4844                 gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 5);
4845             }
4846         }
4848         // add the units menu
4849         gtk_widget_show(us);
4850         gtk_box_pack_start(GTK_BOX(tbl), us, FALSE, FALSE, 1);
4851         gtk_object_set_data(GTK_OBJECT(tbl), "units", us);
4852     }
4854     // letter rotation
4855     aux_toolbox_space(tbl, AUX_BETWEEN_BUTTON_GROUPS);
4856     {
4857         {
4858             GtkWidget *image = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_ROTATION);
4859             GtkWidget *hb = gtk_hbox_new(FALSE, 1);
4860             gtk_container_add (GTK_CONTAINER (hb), image);
4861             gtk_widget_show(image);
4862             gtk_box_pack_start (GTK_BOX (tbl), hb, FALSE, FALSE, 0);
4863         }
4864         {
4865             GtkWidget *hb = sp_tb_spinbutton((""), _("Letter rotation"),
4866                                              "tools.text", "letter_rotation", 0,
4867                                              us, tbl, FALSE, NULL,
4868                                              -180.0, 180.0, 0.1, 0.1,
4869                                              sp_text_letter_rotation_changed, 0.1, 1);
4870             gtk_widget_set_size_request (hb, 45, 0);
4871             gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, 6);
4872         }
4873         // rotation degree label
4874         {
4875             GtkWidget *label = gtk_widget_new (GTK_TYPE_LABEL, "label", "\302\260", "xalign", 0.0, NULL);
4876             gtk_box_pack_start(GTK_BOX(tbl), label, FALSE, FALSE, 0);
4877         }
4878     }
4880     // Remove Manual Kerns
4881     {
4882         GtkWidget *px = sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_TEXT_REMOVE_KERNS);
4883         GtkWidget *button = gtk_button_new ();
4884         gtk_container_add (GTK_CONTAINER (button), px);
4885         gtk_widget_show(button);
4886         gtk_tooltips_set_tip (tt, button, _("Remove manual kerns"), NULL);
4887         gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
4888         gtk_widget_set_sensitive(button, TRUE);
4889         gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS);
4890     }
4891 #endif
4893     gtk_widget_show_all (tbl);
4894     return tbl;
4896 } // end of sp_text_toolbox_new()
4898 }//<unnamed> namespace
4901 //#########################
4902 //##      Connector      ##
4903 //#########################
4905 static void sp_connector_path_set_avoid(void)
4907     cc_selection_set_avoid(true);
4911 static void sp_connector_path_set_ignore(void)
4913     cc_selection_set_avoid(false);
4918 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4920     // quit if run by the _changed callbacks
4921     if (g_object_get_data( tbl, "freeze" )) {
4922         return;
4923     }
4925     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4926     SPDocument *doc = sp_desktop_document(desktop);
4928     if (!sp_document_get_undo_sensitive(doc))
4929     {
4930         return;
4931     }
4933     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4935     if ( repr->attribute("inkscape:connector-spacing") ) {
4936         gdouble priorValue = gtk_adjustment_get_value(adj);
4937         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4938         if ( priorValue == gtk_adjustment_get_value(adj) ) {
4939             return;
4940         }
4941     } else if ( adj->value == defaultConnSpacing ) {
4942         return;
4943     }
4945     // in turn, prevent callbacks from responding
4946     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4948     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4949     SP_OBJECT(desktop->namedview)->updateRepr();
4951     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4952     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4953         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4954         NR::Matrix m = NR::identity();
4955         avoid_item_move(&m, item);
4956     }
4958     if (items) {
4959         g_slist_free(items);
4960     }
4962     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4963             _("Change connector spacing"));
4965     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4967     spinbutton_defocus(GTK_OBJECT(tbl));
4970 static void sp_connector_graph_layout(void)
4972     if (!SP_ACTIVE_DESKTOP) return;
4974     // hack for clones, see comment in align-and-distribute.cpp
4975     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4976     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4978     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
4980     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
4982     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
4985 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4987     if ( gtk_toggle_action_get_active( act ) ) {
4988         prefs_set_string_attribute("tools.connector", "directedlayout",
4989                 "true");
4990     } else {
4991         prefs_set_string_attribute("tools.connector", "directedlayout",
4992                 "false");
4993     }
4996 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4998     if ( gtk_toggle_action_get_active( act ) ) {
4999         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5000                 "true");
5001     } else {
5002         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5003                 "false");
5004     }
5008 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5010     prefs_set_double_attribute("tools.connector", "length", adj->value);
5011     spinbutton_defocus(GTK_OBJECT(tbl));
5014 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5015                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5016                                             bool /*is_interactive*/, gpointer data)
5018     GtkWidget *tbl = GTK_WIDGET(data);
5020     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5021         return;
5022     }
5023     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5024         return;
5025     }
5027     GtkAdjustment *adj = (GtkAdjustment*)
5028             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5029     gdouble spacing = defaultConnSpacing;
5030     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5032     gtk_adjustment_set_value(adj, spacing);
5036 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5037     NULL, /* child_added */
5038     NULL, /* child_removed */
5039     connector_tb_event_attr_changed,
5040     NULL, /* content_changed */
5041     NULL  /* order_changed */
5042 };
5045 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5047     {
5048         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5049                                           _("Avoid"),
5050                                           _("Make connectors avoid selected objects"),
5051                                           "connector_avoid",
5052                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5053         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5054         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5055     }
5057     {
5058         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5059                                           _("Ignore"),
5060                                           _("Make connectors ignore selected objects"),
5061                                           "connector_ignore",
5062                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5063         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5064         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5065     }
5067     EgeAdjustmentAction* eact = 0;
5069     // Spacing spinbox
5070     eact = create_adjustment_action( "ConnectorSpacingAction",
5071                                      _("Connector Spacing"), _("Spacing:"),
5072                                      _("The amount of space left around objects by auto-routing connectors"),
5073                                      "tools.connector", "spacing", defaultConnSpacing,
5074                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5075                                      0, 100, 1.0, 10.0,
5076                                      0, 0, 0,
5077                                      connector_spacing_changed, 1, 0 );
5078     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5080     // Graph (connector network) layout
5081     {
5082         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5083                                           _("Graph"),
5084                                           _("Nicely arrange selected connector network"),
5085                                           "graph_layout",
5086                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5087         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5088         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5089     }
5091     // Default connector length spinbox
5092     eact = create_adjustment_action( "ConnectorLengthAction",
5093                                      _("Connector Length"), _("Length:"),
5094                                      _("Ideal length for connectors when layout is applied"),
5095                                      "tools.connector", "length", 100,
5096                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5097                                      10, 1000, 10.0, 100.0,
5098                                      0, 0, 0,
5099                                      connector_length_changed, 1, 0 );
5100     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5103     // Directed edges toggle button
5104     {
5105         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5106                                                       _("Downwards"),
5107                                                       _("Make connectors with end-markers (arrows) point downwards"),
5108                                                       "directed_graph",
5109                                                       Inkscape::ICON_SIZE_DECORATION );
5110         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5112         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5113         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5114                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5116         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5117     }
5119     // Avoid overlaps toggle button
5120     {
5121         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5122                                                       _("Remove overlaps"),
5123                                                       _("Do not allow overlapping shapes"),
5124                                                       "remove_overlaps",
5125                                                       Inkscape::ICON_SIZE_DECORATION );
5126         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5128         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5129         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5130                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5132         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5133     }
5135     // Code to watch for changes to the connector-spacing attribute in
5136     // the XML.
5137     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5138     g_assert(repr != NULL);
5140     purge_repr_listener( holder, holder );
5142     if (repr) {
5143         g_object_set_data( holder, "repr", repr );
5144         Inkscape::GC::anchor(repr);
5145         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5146         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5147     }
5148 } // end of sp_connector_toolbox_prep()
5151 //#########################
5152 //##     Paintbucket     ##
5153 //#########################
5155 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5157     gint channels = ege_select_one_action_get_active( act );
5158     flood_channels_set_channels( channels );
5161 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5163     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5166 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5168     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5171 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5173     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5174     SPUnit const *unit = tracker->getActiveUnit();
5176     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5178     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5181 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5183     // FIXME: make defaults settable via Inkscape Options
5184     struct KeyValue {
5185         char const *key;
5186         double value;
5187     } const key_values[] = {
5188         {"threshold", 15},
5189         {"offset", 0.0}
5190     };
5192     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5193         KeyValue const &kv = key_values[i];
5194         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5195         if ( adj ) {
5196             gtk_adjustment_set_value(adj, kv.value);
5197         }
5198     }
5200     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5201     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5202     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5203     ege_select_one_action_set_active( autogap_action, 0 );
5206 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5208     EgeAdjustmentAction* eact = 0;
5210     {
5211         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5213         GList* items = 0;
5214         gint count = 0;
5215         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5216         {
5217             GtkTreeIter iter;
5218             gtk_list_store_append( model, &iter );
5219             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5220             count++;
5221         }
5222         g_list_free( items );
5223         items = 0;
5224         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5225         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5226         ege_select_one_action_set_appearance( act1, "compact" );
5227         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5228         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5229         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5230         g_object_set_data( holder, "channels_action", act1 );
5231     }
5233     // Spacing spinbox
5234     {
5235         eact = create_adjustment_action(
5236             "ThresholdAction",
5237             _("Fill Threshold"), _("Threshold:"),
5238             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5239             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5240             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5241             0, 0, 0,
5242             paintbucket_threshold_changed, 1, 0 );
5244         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5245     }
5247     // Create the units menu.
5248     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5249     tracker->setActiveUnit(sp_unit_get_by_abbreviation(prefs_get_string_attribute("tools.paintbucket", "offsetunits")));
5250     g_object_set_data( holder, "tracker", tracker );
5251     {
5252         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5253         gtk_action_group_add_action( mainActions, act );
5254     }
5256     // Offset spinbox
5257     {
5258         eact = create_adjustment_action(
5259             "OffsetAction",
5260             _("Grow/shrink by"), _("Grow/shrink by:"),
5261             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5262             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5263             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5264             0, 0, 0,
5265             paintbucket_offset_changed, 1, 2);
5266         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5268         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5269     }
5271     /* Auto Gap */
5272     {
5273         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5275         GList* items = 0;
5276         gint count = 0;
5277         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5278         {
5279             GtkTreeIter iter;
5280             gtk_list_store_append( model, &iter );
5281             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5282             count++;
5283         }
5284         g_list_free( items );
5285         items = 0;
5286         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5287         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5288         ege_select_one_action_set_appearance( act2, "compact" );
5289         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5290         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5291         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5292         g_object_set_data( holder, "autogap_action", act2 );
5293     }
5295     /* Reset */
5296     {
5297         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5298                                           _("Defaults"),
5299                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5300                                           GTK_STOCK_CLEAR );
5301         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5302         gtk_action_group_add_action( mainActions, act );
5303         gtk_action_set_sensitive( act, TRUE );
5304     }
5308 /*
5309   Local Variables:
5310   mode:c++
5311   c-file-style:"stroustrup"
5312   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5313   indent-tabs-mode:nil
5314   fill-column:99
5315   End:
5316 */
5317 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :