Code

calligraphic profiles are now read from the preferences
[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 <cstring>
31 #include <string>
33 #include <gtkmm.h>
34 #include <gtk/gtk.h>
35 #include <iostream>
36 #include <sstream>
38 #include "widgets/button.h"
39 #include "widgets/widget-sizes.h"
40 #include "widgets/spw-utilities.h"
41 #include "widgets/spinbutton-events.h"
42 #include "dialogs/text-edit.h"
43 #include "dialogs/dialog-events.h"
45 #include "ui/widget/style-swatch.h"
47 #include "prefs-utils.h"
48 #include "verbs.h"
49 #include "sp-namedview.h"
50 #include "desktop.h"
51 #include "desktop-handles.h"
52 #include "xml/repr.h"
53 #include "xml/node-event-vector.h"
54 #include <glibmm/i18n.h>
55 #include "helper/unit-menu.h"
56 #include "helper/units.h"
58 #include "inkscape.h"
59 #include "conn-avoid-ref.h"
62 #include "select-toolbar.h"
63 #include "gradient-toolbar.h"
65 #include "connector-context.h"
66 #include "node-context.h"
67 #include "shape-editor.h"
68 #include "tweak-context.h"
69 #include "sp-rect.h"
70 #include "box3d.h"
71 #include "box3d-context.h"
72 #include "sp-star.h"
73 #include "sp-spiral.h"
74 #include "sp-ellipse.h"
75 #include "sp-text.h"
76 #include "sp-flowtext.h"
77 #include "style.h"
78 #include "selection.h"
79 #include "selection-chemistry.h"
80 #include "document-private.h"
81 #include "desktop-style.h"
82 #include "../libnrtype/font-lister.h"
83 #include "../libnrtype/font-instance.h"
84 #include "../connection-pool.h"
85 #include "../prefs-utils.h"
86 #include "../inkscape-stock.h"
87 #include "icon.h"
88 #include "graphlayout/graphlayout.h"
90 #include "mod360.h"
92 #include "toolbox.h"
94 #include "flood-context.h"
96 #include "ink-action.h"
97 #include "ege-adjustment-action.h"
98 #include "ege-output-action.h"
99 #include "ege-select-one-action.h"
100 #include "helper/unit-tracker.h"
102 #include "svg/css-ostringstream.h"
104 using Inkscape::UnitTracker;
106 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
107 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
109 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
122 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
128 static struct {
129     gchar const *type_name;
130     gchar const *data_name;
131     sp_verb_t verb;
132     sp_verb_t doubleclick_verb;
133 } const tools[] = {
134     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
135     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
136     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
137     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
138     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
139     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
140     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
141     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
142     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
143     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
144     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
145     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
146     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
147     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
148     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
149     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
150     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
151     { NULL, NULL, 0, 0 }
152 };
154 static struct {
155     gchar const *type_name;
156     gchar const *data_name;
157     GtkWidget *(*create_func)(SPDesktop *desktop);
158     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
159     gchar const *ui_name;
160     gint swatch_verb_id;
161     gchar const *swatch_tool;
162     gchar const *swatch_tip;
163 } const aux_toolboxes[] = {
164     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
165       SP_VERB_INVALID, 0, 0},
166     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
167       SP_VERB_INVALID, 0, 0},
168     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
169       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
170     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
171       SP_VERB_INVALID, 0, 0},
172     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
173       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     _("Style of new stars")},
174     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
175       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     _("Style of new rectangles")},
176     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
177       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    _("Style of new 3D boxes")},
178     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
179       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      _("Style of new ellipses")},
180     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
181       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   _("Style of new spirals")},
182     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
183       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
184     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
185       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    _("Style of new paths created by Pen")},
186     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
187       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
188     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
189       SP_VERB_INVALID, 0, 0},
190     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
191       SP_VERB_INVALID, 0, 0},
192     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
193       SP_VERB_INVALID, 0, 0},
194     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
195       SP_VERB_INVALID, 0, 0},
196     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
197       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
198     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
199 };
202 static gchar const * ui_descr =
203         "<ui>"
204         "  <toolbar name='SelectToolbar'>"
205         "    <toolitem action='EditSelectAll' />"
206         "    <toolitem action='EditSelectAllInAllLayers' />"
207         "    <toolitem action='EditDeselect' />"
208         "    <separator />"
209         "    <toolitem action='ObjectRotate90CCW' />"
210         "    <toolitem action='ObjectRotate90' />"
211         "    <toolitem action='ObjectFlipHorizontally' />"
212         "    <toolitem action='ObjectFlipVertically' />"
213         "    <separator />"
214         "    <toolitem action='SelectionToBack' />"
215         "    <toolitem action='SelectionLower' />"
216         "    <toolitem action='SelectionRaise' />"
217         "    <toolitem action='SelectionToFront' />"
218         "    <separator />"
219         "    <toolitem action='XAction' />"
220         "    <toolitem action='YAction' />"
221         "    <toolitem action='WidthAction' />"
222         "    <toolitem action='LockAction' />"
223         "    <toolitem action='HeightAction' />"
224         "    <toolitem action='UnitsAction' />"
225         "    <separator />"
226         "    <toolitem action='transform_affect_label' />"
227         "    <toolitem action='transform_stroke' />"
228         "    <toolitem action='transform_corners' />"
229         "    <toolitem action='transform_gradient' />"
230         "    <toolitem action='transform_pattern' />"
231         "  </toolbar>"
233         "  <toolbar name='NodeToolbar'>"
234         "    <toolitem action='NodeInsertAction' />"
235         "    <toolitem action='NodeDeleteAction' />"
236         "    <separator />"
237         "    <toolitem action='NodeJoinAction' />"
238         "    <toolitem action='NodeJoinSegmentAction' />"
239         "    <toolitem action='NodeDeleteSegmentAction' />"
240         "    <toolitem action='NodeBreakAction' />"
241         "    <separator />"
242         "    <toolitem action='NodeCuspAction' />"
243         "    <toolitem action='NodeSmoothAction' />"
244         "    <toolitem action='NodeSymmetricAction' />"
245         "    <separator />"
246         "    <toolitem action='NodeLineAction' />"
247         "    <toolitem action='NodeCurveAction' />"
248         "    <separator />"
249         "    <toolitem action='ObjectToPath' />"
250         "    <toolitem action='StrokeToPath' />"
251         "    <separator />"
252         "    <toolitem action='NodesShowHandlesAction' />"
253         "    <toolitem action='NodesShowHelperpath' />"
254         "    <separator />"
255         "    <toolitem action='EditNextLPEParameterAction' />"
256         "    <separator />"
257         "    <toolitem action='NodeXAction' />"
258         "    <toolitem action='NodeYAction' />"
259         "    <toolitem action='NodeUnitsAction' />"
260         "  </toolbar>"
262         "  <toolbar name='TweakToolbar'>"
263         "    <toolitem action='TweakWidthAction' />"
264         "    <separator />"
265         "    <toolitem action='TweakForceAction' />"
266         "    <toolitem action='TweakPressureAction' />"
267         "    <separator />"
268         "    <toolitem action='TweakModeAction' />"
269         "    <separator />"
270         "    <toolitem action='TweakFidelityAction' />"
271         "    <separator />"
272         "    <toolitem action='TweakChannelsLabel' />"
273         "    <toolitem action='TweakDoH' />"
274         "    <toolitem action='TweakDoS' />"
275         "    <toolitem action='TweakDoL' />"
276         "    <toolitem action='TweakDoO' />"
277         "  </toolbar>"
279         "  <toolbar name='ZoomToolbar'>"
280         "    <toolitem action='ZoomIn' />"
281         "    <toolitem action='ZoomOut' />"
282         "    <separator />"
283         "    <toolitem action='Zoom1:0' />"
284         "    <toolitem action='Zoom1:2' />"
285         "    <toolitem action='Zoom2:1' />"
286         "    <separator />"
287         "    <toolitem action='ZoomSelection' />"
288         "    <toolitem action='ZoomDrawing' />"
289         "    <toolitem action='ZoomPage' />"
290         "    <toolitem action='ZoomPageWidth' />"
291         "    <separator />"
292         "    <toolitem action='ZoomPrev' />"
293         "    <toolitem action='ZoomNext' />"
294         "  </toolbar>"
296         "  <toolbar name='StarToolbar'>"
297         "    <separator />"
298         "    <toolitem action='StarStateAction' />"
299         "    <separator />"
300         "    <toolitem action='FlatAction' />"
301         "    <separator />"
302         "    <toolitem action='MagnitudeAction' />"
303         "    <toolitem action='SpokeAction' />"
304         "    <toolitem action='RoundednessAction' />"
305         "    <toolitem action='RandomizationAction' />"
306         "    <separator />"
307         "    <toolitem action='StarResetAction' />"
308         "  </toolbar>"
310         "  <toolbar name='RectToolbar'>"
311         "    <toolitem action='RectStateAction' />"
312         "    <toolitem action='RectWidthAction' />"
313         "    <toolitem action='RectHeightAction' />"
314         "    <toolitem action='RadiusXAction' />"
315         "    <toolitem action='RadiusYAction' />"
316         "    <toolitem action='RectUnitsAction' />"
317         "    <separator />"
318         "    <toolitem action='RectResetAction' />"
319         "  </toolbar>"
321         "  <toolbar name='3DBoxToolbar'>"
322         "    <toolitem action='3DBoxAngleXAction' />"
323         "    <toolitem action='3DBoxVPXStateAction' />"
324         "    <separator />"
325         "    <toolitem action='3DBoxAngleYAction' />"
326         "    <toolitem action='3DBoxVPYStateAction' />"
327         "    <separator />"
328         "    <toolitem action='3DBoxAngleZAction' />"
329         "    <toolitem action='3DBoxVPZStateAction' />"
330         "  </toolbar>"
332         "  <toolbar name='SpiralToolbar'>"
333         "    <toolitem action='SpiralStateAction' />"
334         "    <toolitem action='SpiralRevolutionAction' />"
335         "    <toolitem action='SpiralExpansionAction' />"
336         "    <toolitem action='SpiralT0Action' />"
337         "    <separator />"
338         "    <toolitem action='SpiralResetAction' />"
339         "  </toolbar>"
341         "  <toolbar name='PenToolbar'>"
342         "  </toolbar>"
344         "  <toolbar name='PencilToolbar'>"
345         "  </toolbar>"
347         "  <toolbar name='CalligraphyToolbar'>"
348         "    <separator />"
349         "    <toolitem action='SetProfileAction'/>"
350         "    <separator />"
351         "    <toolitem action='CalligraphyWidthAction' />"
352         "    <toolitem action='PressureAction' />"
353         "    <toolitem action='TraceAction' />"
354         "    <toolitem action='ThinningAction' />"
355         "    <separator />"
356         "    <toolitem action='AngleAction' />"
357         "    <toolitem action='TiltAction' />"
358         "    <toolitem action='FixationAction' />"
359         "    <separator />"
360         "    <toolitem action='CapRoundingAction' />"
361         "    <separator />"
362         "    <toolitem action='TremorAction' />"
363         "    <toolitem action='WiggleAction' />"
364         "    <toolitem action='MassAction' />"
365         "    <separator />"
366         "  </toolbar>"
368         "  <toolbar name='ArcToolbar'>"
369         "    <toolitem action='ArcStateAction' />"
370         "    <separator />"
371         "    <toolitem action='ArcStartAction' />"
372         "    <toolitem action='ArcEndAction' />"
373         "    <separator />"
374         "    <toolitem action='ArcOpenAction' />"
375         "    <separator />"
376         "    <toolitem action='ArcResetAction' />"
377         "    <separator />"
378         "  </toolbar>"
380         "  <toolbar name='PaintbucketToolbar'>"
381         "    <toolitem action='ChannelsAction' />"
382         "    <separator />"
383         "    <toolitem action='ThresholdAction' />"
384         "    <separator />"
385         "    <toolitem action='OffsetAction' />"
386         "    <toolitem action='PaintbucketUnitsAction' />"
387         "    <separator />"
388         "    <toolitem action='AutoGapAction' />"
389         "    <separator />"
390         "    <toolitem action='PaintbucketResetAction' />"
391         "  </toolbar>"
393         "  <toolbar name='DropperToolbar'>"
394         "    <toolitem action='DropperOpacityAction' />"
395         "    <toolitem action='DropperPickAlphaAction' />"
396         "    <toolitem action='DropperSetAlphaAction' />"
397         "  </toolbar>"
399         "  <toolbar name='ConnectorToolbar'>"
400         "    <toolitem action='ConnectorAvoidAction' />"
401         "    <toolitem action='ConnectorIgnoreAction' />"
402         "    <toolitem action='ConnectorSpacingAction' />"
403         "    <toolitem action='ConnectorGraphAction' />"
404         "    <toolitem action='ConnectorLengthAction' />"
405         "    <toolitem action='ConnectorDirectedAction' />"
406         "    <toolitem action='ConnectorOverlapAction' />"
407         "  </toolbar>"
409         "</ui>"
412 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
414 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
416 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
417 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
419 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
420 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
422 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
423 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
425 /* Global text entry widgets necessary for update */
426 /* GtkWidget *dropper_rgb_entry,
427           *dropper_opacity_entry ; */
428 // should be made a private member once this is converted to class
430 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
431     connection->disconnect();
432     delete connection;
435 static void purge_repr_listener( GObject* obj, GObject* tbl )
437     (void)obj;
438     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
439     if (oldrepr) { // remove old listener
440         sp_repr_remove_listener_by_data(oldrepr, tbl);
441         Inkscape::GC::release(oldrepr);
442         oldrepr = 0;
443         g_object_set_data( tbl, "repr", NULL );
444     }
447 GtkWidget *
448 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
449                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
450                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
452     SPAction *action = verb->get_action(view);
453     if (!action) return NULL;
455     SPAction *doubleclick_action;
456     if (doubleclick_verb)
457         doubleclick_action = doubleclick_verb->get_action(view);
458     else
459         doubleclick_action = NULL;
461     /* fixme: Handle sensitive/unsensitive */
462     /* fixme: Implement sp_button_new_from_action */
463     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
464     gtk_widget_show(b);
465     gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
467     return b;
470 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
471                                            Inkscape::UI::View::View *view, GtkTooltips *tt)
473     return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
476 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
477                                                    Inkscape::UI::View::View *view, GtkTooltips *tt)
479     return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
483 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
485     SPAction* targetAction = SP_ACTION(user_data);
486     if ( targetAction ) {
487         sp_action_perform( targetAction, NULL );
488     }
491 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
493     if ( data ) {
494         GtkAction* act = GTK_ACTION(data);
495         gtk_action_set_sensitive( act, sensitive );
496     }
499 static SPActionEventVector action_event_vector = {
500     {NULL},
501     NULL,
502     NULL,
503     sp_action_action_set_sensitive,
504     NULL,
505     NULL
506 };
508 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
510     GtkAction* act = 0;
512     SPAction* targetAction = verb->get_action(view);
513     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
514     act = GTK_ACTION(inky);
515     gtk_action_set_sensitive( act, targetAction->sensitive );
517     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
519     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
520     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
522     return act;
525 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
527     Inkscape::UI::View::View *view = desktop;
528     gint verbsToUse[] = {
529         // disabled until we have icons for them:
530         //find
531         //SP_VERB_EDIT_TILE,
532         //SP_VERB_EDIT_UNTILE,
533         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
534         SP_VERB_DIALOG_DISPLAY,
535         SP_VERB_DIALOG_FILL_STROKE,
536         SP_VERB_DIALOG_NAMEDVIEW,
537         SP_VERB_DIALOG_TEXT,
538         SP_VERB_DIALOG_XML_EDITOR,
539         SP_VERB_EDIT_CLONE,
540         SP_VERB_EDIT_COPY,
541         SP_VERB_EDIT_CUT,
542         SP_VERB_EDIT_DUPLICATE,
543         SP_VERB_EDIT_PASTE,
544         SP_VERB_EDIT_REDO,
545         SP_VERB_EDIT_UNDO,
546         SP_VERB_EDIT_UNLINK_CLONE,
547         SP_VERB_FILE_EXPORT,
548         SP_VERB_FILE_IMPORT,
549         SP_VERB_FILE_NEW,
550         SP_VERB_FILE_OPEN,
551         SP_VERB_FILE_PRINT,
552         SP_VERB_FILE_SAVE,
553         SP_VERB_OBJECT_TO_CURVE,
554         SP_VERB_SELECTION_GROUP,
555         SP_VERB_SELECTION_OUTLINE,
556         SP_VERB_SELECTION_UNGROUP,
557         SP_VERB_ZOOM_1_1,
558         SP_VERB_ZOOM_1_2,
559         SP_VERB_ZOOM_2_1,
560         SP_VERB_ZOOM_DRAWING,
561         SP_VERB_ZOOM_IN,
562         SP_VERB_ZOOM_NEXT,
563         SP_VERB_ZOOM_OUT,
564         SP_VERB_ZOOM_PAGE,
565         SP_VERB_ZOOM_PAGE_WIDTH,
566         SP_VERB_ZOOM_PREV,
567         SP_VERB_ZOOM_SELECTION,
568     };
570     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
571     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
573     static std::map<SPDesktop*, GtkActionGroup*> groups;
574     GtkActionGroup* mainActions = 0;
575     if ( groups.find(desktop) != groups.end() ) {
576         mainActions = groups[desktop];
577     }
579     if ( !mainActions ) {
580         mainActions = gtk_action_group_new("main");
581         groups[desktop] = mainActions;
582     }
584     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
585         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
586         if ( verb ) {
587             if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
588                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
589                 gtk_action_group_add_action( mainActions, act );
590             }
591         }
592     }
594     return mainActions;
598 GtkWidget *
599 sp_tool_toolbox_new()
601     GtkTooltips *tt = gtk_tooltips_new();
602     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
604     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
605     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
607     gtk_widget_set_sensitive(tb, FALSE);
609     GtkWidget *hb = gtk_handle_box_new();
610     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
611     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
612     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
614     gtk_container_add(GTK_CONTAINER(hb), tb);
615     gtk_widget_show(GTK_WIDGET(tb));
617     sigc::connection* conn = new sigc::connection;
618     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
620     return hb;
623 static void
624 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
626     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
627     gtk_widget_queue_resize(child);
630 static void
631 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
633     g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
634     gtk_widget_queue_resize(child);
637 GtkWidget *
638 sp_aux_toolbox_new()
640     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
642     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
644     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
646     gtk_widget_set_sensitive(tb, FALSE);
648     GtkWidget *hb = gtk_handle_box_new();
649     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
650     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
651     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
653     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
654     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
656     gtk_container_add(GTK_CONTAINER(hb), tb);
657     gtk_widget_show(GTK_WIDGET(tb));
659     sigc::connection* conn = new sigc::connection;
660     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
662     return hb;
665 //####################################
666 //# Commands Bar
667 //####################################
669 GtkWidget *
670 sp_commands_toolbox_new()
672     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
674     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
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 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
852     bool show = gtk_toggle_action_get_active( act );
853     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
854     ShapeEditor *shape_editor = get_current_shape_editor();
855     if (shape_editor) shape_editor->show_helperpath(show);
858 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
859     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
862 /* is called when the node selection is modified */
863 static void
864 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
866     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
867     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
868     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
869     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
871     // quit if run by the attr_changed listener
872     if (g_object_get_data( tbl, "freeze" )) {
873         return;
874     }
876     // in turn, prevent listener from responding
877     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
879     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
880     SPUnit const *unit = tracker->getActiveUnit();
882     ShapeEditor *shape_editor = get_current_shape_editor();
883     if (shape_editor && shape_editor->has_nodepath()) {
884         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
885         int n_selected = 0;
886         if (nodepath) {
887             n_selected = nodepath->numSelected();
888         }
890         if (n_selected == 0) {
891             gtk_action_set_sensitive(xact, FALSE);
892             gtk_action_set_sensitive(yact, FALSE);
893         } else {
894             gtk_action_set_sensitive(xact, TRUE);
895             gtk_action_set_sensitive(yact, TRUE);
896             NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
897             NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
899             if (n_selected == 1) {
900                 NR::Point sel_node = nodepath->singleSelectedCoords();
901                 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
902                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
903                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
904                 }
905             } else {
906                 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
907                 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
908                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
909                     /* Note: Currently x and y will always have a value, even if the coordinates of the
910                        selected nodes don't coincide (in this case we use the coordinates of the center
911                        of the bounding box). So the entries are never set to zero. */
912                     // FIXME: Maybe we should clear the entry if several nodes are selected
913                     //        instead of providing a kind of average value
914                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
915                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
916                 }
917             }
918         }
919     } else {
920         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
921         gtk_action_set_sensitive(xact, FALSE);
922         gtk_action_set_sensitive(yact, FALSE);
923     }
925     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
928 static void
929 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
931     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
933     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
934     SPUnit const *unit = tracker->getActiveUnit();
936     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
937         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
938     }
940     // quit if run by the attr_changed listener
941     if (g_object_get_data( tbl, "freeze" )) {
942         return;
943     }
945     // in turn, prevent listener from responding
946     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
948     ShapeEditor *shape_editor = get_current_shape_editor();
949     if (shape_editor && shape_editor->has_nodepath()) {
950         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
951         if (!strcmp(value_name, "x")) {
952             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
953         }
954         if (!strcmp(value_name, "y")) {
955             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
956         }
957     }
959     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
962 static void
963 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
965     sp_node_path_value_changed(adj, tbl, "x");
968 static void
969 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
971     sp_node_path_value_changed(adj, tbl, "y");
974 //################################
975 //##    Node Editing Toolbox    ##
976 //################################
978 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
980     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
981     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
982     g_object_set_data( holder, "tracker", tracker );
984     {
985         InkAction* inky = ink_action_new( "NodeInsertAction",
986                                           _("Insert node"),
987                                           _("Insert new nodes into selected segments"),
988                                           "node_insert",
989                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
990         g_object_set( inky, "short_label", _("Insert"), NULL );
991         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
992         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
993     }
995     {
996         InkAction* inky = ink_action_new( "NodeDeleteAction",
997                                           _("Delete node"),
998                                           _("Delete selected nodes"),
999                                           "node_delete",
1000                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1001         g_object_set( inky, "short_label", _("Delete"), NULL );
1002         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1003         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1004     }
1006     {
1007         InkAction* inky = ink_action_new( "NodeJoinAction",
1008                                           _("Join endnodes"),
1009                                           _("Join selected endnodes"),
1010                                           "node_join",
1011                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1012         g_object_set( inky, "short_label", _("Join"), NULL );
1013         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1014         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1015     }
1017     {
1018         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1019                                           _("Join Segment"),
1020                                           _("Join selected endnodes with a new segment"),
1021                                           "node_join_segment",
1022                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1023         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1024         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1025     }
1027     {
1028         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1029                                           _("Delete Segment"),
1030                                           _("Split path between two non-endpoint nodes"),
1031                                           "node_delete_segment",
1032                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1033         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1034         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1035     }
1037     {
1038         InkAction* inky = ink_action_new( "NodeBreakAction",
1039                                           _("Node Break"),
1040                                           _("Break path at selected nodes"),
1041                                           "node_break",
1042                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1043         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1044         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1045     }
1047     {
1048         InkAction* inky = ink_action_new( "NodeCuspAction",
1049                                           _("Node Cusp"),
1050                                           _("Make selected nodes corner"),
1051                                           "node_cusp",
1052                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1053         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1054         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1055     }
1057     {
1058         InkAction* inky = ink_action_new( "NodeSmoothAction",
1059                                           _("Node Smooth"),
1060                                           _("Make selected nodes smooth"),
1061                                           "node_smooth",
1062                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1063         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1064         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1065     }
1067     {
1068         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1069                                           _("Node Symmetric"),
1070                                           _("Make selected nodes symmetric"),
1071                                           "node_symmetric",
1072                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1073         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1074         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1075     }
1077     {
1078         InkAction* inky = ink_action_new( "NodeLineAction",
1079                                           _("Node Line"),
1080                                           _("Make selected segments lines"),
1081                                           "node_line",
1082                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1083         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1084         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1085     }
1087     {
1088         InkAction* inky = ink_action_new( "NodeCurveAction",
1089                                           _("Node Curve"),
1090                                           _("Make selected segments curves"),
1091                                           "node_curve",
1092                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1093         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1094         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1095     }
1097     {
1098         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1099                                                       _("Show Handles"),
1100                                                       _("Show the Bezier handles of selected nodes"),
1101                                                       "nodes_show_handles",
1102                                                       Inkscape::ICON_SIZE_DECORATION );
1103         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1104         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1105         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1106     }
1108     {
1109         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1110                                                       _("Show Outline"),
1111                                                       _("Show the outline of the path"),
1112                                                       "nodes_show_helperpath",
1113                                                       Inkscape::ICON_SIZE_DECORATION );
1114         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1115         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1116         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1117     }
1119     {
1120         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1121                                           _("Next Path Effect Parameter"),
1122                                           _("Show next Path Effect parameter for editing"),
1123                                           "edit_next_parameter",
1124                                           Inkscape::ICON_SIZE_DECORATION );
1125         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1126         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1127     }
1129     /* X 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( "NodeXAction",
1135                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1136                                          "tools.nodes", "Xcoord", 0,
1137                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1138                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1139                                          labels, values, G_N_ELEMENTS(labels),
1140                                          sp_node_path_x_value_changed );
1141         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1142         g_object_set_data( holder, "nodes_x_action", eact );
1143         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1144         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1145     }
1147     /* Y coord of selected node(s) */
1148     {
1149         EgeAdjustmentAction* eact = 0;
1150         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1151         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1152         eact = create_adjustment_action( "NodeYAction",
1153                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1154                                          "tools.nodes", "Ycoord", 0,
1155                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1156                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1157                                          labels, values, G_N_ELEMENTS(labels),
1158                                          sp_node_path_y_value_changed );
1159         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1160         g_object_set_data( holder, "nodes_y_action", eact );
1161         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1162         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1163     }
1165     // add the units menu
1166     {
1167         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1168         gtk_action_group_add_action( mainActions, act );
1169     }
1171     sigc::connection *connection = new sigc::connection (
1172         desktop->connectToolSubselectionChanged(sigc::bind (sigc::ptr_fun(sp_node_toolbox_coord_changed), (GObject *)holder))
1173         );
1175     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1176     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1177 } // end of sp_node_toolbox_prep()
1180 //########################
1181 //##    Zoom Toolbox    ##
1182 //########################
1184 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1186     // no custom GtkAction setup needed
1187 } // end of sp_zoom_toolbox_prep()
1189 void
1190 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1192     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")));
1196 void
1197 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1199     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")));
1202 void
1203 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1205     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")));
1208 static void
1209 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1211     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1212     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1214     if (old_desktop) {
1215         GList *children, *iter;
1217         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1218         for ( iter = children ; iter ; iter = iter->next ) {
1219             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1220         }
1221         g_list_free(children);
1222     }
1224     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1226     if (desktop) {
1227         gtk_widget_set_sensitive(toolbox, TRUE);
1228         setup_func(toolbox, desktop);
1229         update_func(desktop, desktop->event_context, toolbox);
1230         *conn = desktop->connectEventContextChanged
1231             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1232     } else {
1233         gtk_widget_set_sensitive(toolbox, FALSE);
1234     }
1236 } // end of toolbox_set_desktop()
1239 static void
1240 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1242     GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1243     gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1244     if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1245         // "toolbox.tools" was not set. Fallback to older value
1246         shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1248         // Copy the setting forwards
1249         prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1250     }
1251     Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1253     for (int i = 0 ; tools[i].type_name ; i++ ) {
1254         GtkWidget *button =
1255             sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1256                                                               SP_BUTTON_TYPE_TOGGLE,
1257                                                               Inkscape::Verb::get(tools[i].verb),
1258                                                               Inkscape::Verb::get(tools[i].doubleclick_verb),
1259                                                               desktop,
1260                                                               tooltips );
1262         g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1263                            (gpointer)button );
1264     }
1268 static void
1269 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1271     gchar const *const tname = ( eventcontext
1272                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1273                                  : NULL );
1274     for (int i = 0 ; tools[i].type_name ; i++ ) {
1275         SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1276         sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1277     }
1280 static void
1281 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1283     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1284     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1285     GtkUIManager* mgr = gtk_ui_manager_new();
1286     GError* errVal = 0;
1287     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1288     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1290     std::map<std::string, GtkWidget*> dataHolders;
1292     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1293         if ( aux_toolboxes[i].prep_func ) {
1294             // converted to GtkActions and UIManager
1296             GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1297             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1298             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1299             dataHolders[aux_toolboxes[i].type_name] = kludge;
1300             aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1301         } else {
1303             GtkWidget *sub_toolbox = 0;
1304             if (aux_toolboxes[i].create_func == NULL)
1305                 sub_toolbox = sp_empty_toolbox_new(desktop);
1306             else {
1307                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1308             }
1310             gtk_size_group_add_widget( grouper, sub_toolbox );
1312             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1313             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1315         }
1316     }
1318     // Second pass to create toolbars *after* all GtkActions are created
1319     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1320         if ( aux_toolboxes[i].prep_func ) {
1321             // converted to GtkActions and UIManager
1323             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1325             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1326             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1328             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1329             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1330             g_free( tmp );
1331             tmp = 0;
1333             gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1334             Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1335             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1336                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1337             }
1338             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1341             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1343             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1344                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1345                 swatch->setDesktop( desktop );
1346                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1347                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1348                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1349                 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 );
1350             }
1352             gtk_widget_show_all( holder );
1353             sp_set_font_size_smaller( holder );
1355             gtk_size_group_add_widget( grouper, holder );
1357             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1358             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1359         }
1360     }
1362     g_object_unref( G_OBJECT(grouper) );
1365 static void
1366 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1368     gchar const *tname = ( eventcontext
1369                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1370                            : NULL );
1371     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1372         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1373         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1374             gtk_widget_show_all(sub_toolbox);
1375             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1376         } else {
1377             gtk_widget_hide(sub_toolbox);
1378         }
1379     }
1382 static void
1383 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1385     gchar const * descr =
1386         "<ui>"
1387         "  <toolbar name='CommandsToolbar'>"
1388         "    <toolitem action='FileNew' />"
1389         "    <toolitem action='FileOpen' />"
1390         "    <toolitem action='FileSave' />"
1391         "    <toolitem action='FilePrint' />"
1392         "    <separator />"
1393         "    <toolitem action='FileImport' />"
1394         "    <toolitem action='FileExport' />"
1395         "    <separator />"
1396         "    <toolitem action='EditUndo' />"
1397         "    <toolitem action='EditRedo' />"
1398         "    <separator />"
1399         "    <toolitem action='EditCopy' />"
1400         "    <toolitem action='EditCut' />"
1401         "    <toolitem action='EditPaste' />"
1402         "    <separator />"
1403         "    <toolitem action='ZoomSelection' />"
1404         "    <toolitem action='ZoomDrawing' />"
1405         "    <toolitem action='ZoomPage' />"
1406         "    <separator />"
1407         "    <toolitem action='EditDuplicate' />"
1408         "    <toolitem action='EditClone' />"
1409         "    <toolitem action='EditUnlinkClone' />"
1410         "    <separator />"
1411         "    <toolitem action='SelectionGroup' />"
1412         "    <toolitem action='SelectionUnGroup' />"
1413         "    <separator />"
1414         "    <toolitem action='DialogFillStroke' />"
1415         "    <toolitem action='DialogText' />"
1416         "    <toolitem action='DialogXMLEditor' />"
1417         "    <toolitem action='DialogAlignDistribute' />"
1418         "    <separator />"
1419         "    <toolitem action='DialogPreferences' />"
1420         "    <toolitem action='DialogDocumentProperties' />"
1421         "  </toolbar>"
1422         "</ui>";
1423     GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1426     GtkUIManager* mgr = gtk_ui_manager_new();
1427     GError* errVal = 0;
1429     gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1430     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1432     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1433     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1434         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1435     }
1436     gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1437     Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1438     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1441     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1444 static void
1445 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1449 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1451     gtk_widget_show(toolbox_toplevel);
1452     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1454     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1455     if (!shown_toolbox) {
1456         return;
1457     }
1458     gtk_widget_show(toolbox);
1460     gtk_widget_show_all(shown_toolbox);
1463 void
1464 aux_toolbox_space(GtkWidget *tb, gint space)
1466     gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1469 static GtkWidget *
1470 sp_empty_toolbox_new(SPDesktop *desktop)
1472     GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1473     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1474     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1476     gtk_widget_show_all(tbl);
1477     sp_set_font_size_smaller (tbl);
1479     return tbl;
1482 // helper UI functions
1484 GtkWidget *
1485 sp_tb_spinbutton(
1486     gchar *label, gchar const *tooltip,
1487     gchar const *path, gchar const *data, gdouble def,
1488     GtkWidget *us,
1489     GtkWidget *tbl,
1490     gboolean altx, gchar const *altx_mark,
1491     gdouble lower, gdouble upper, gdouble step, gdouble page,
1492     void (*callback)(GtkAdjustment *, GtkWidget *),
1493     gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1495     GtkTooltips *tt = gtk_tooltips_new();
1497     GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1499     GtkWidget *l = gtk_label_new(label);
1500     gtk_widget_show(l);
1501     gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1502     gtk_container_add(GTK_CONTAINER(hb), l);
1504     GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1505                                       lower, upper, step, page, page);
1506     gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1507     if (us)
1508         sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1510     GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1511     gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1512     if (altx)
1513         gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1514     gtk_widget_set_size_request(sb,
1515                                 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1516                                 AUX_SPINBUTTON_HEIGHT);
1517     gtk_widget_show(sb);
1518     gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1519     gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1520     gtk_container_add(GTK_CONTAINER(hb), sb);
1521     gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1523     return hb;
1526 #define MODE_LABEL_WIDTH 70
1528 //########################
1529 //##       Star         ##
1530 //########################
1532 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1534     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1536     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1537         // do not remember prefs if this call is initiated by an undo change, because undoing object
1538         // creation sets bogus values to its attributes before it is deleted
1539         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1540     }
1542     // quit if run by the attr_changed listener
1543     if (g_object_get_data( dataKludge, "freeze" )) {
1544         return;
1545     }
1547     // in turn, prevent listener from responding
1548     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1550     bool modmade = false;
1552     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1553     GSList const *items = selection->itemList();
1554     for (; items != NULL; items = items->next) {
1555         if (SP_IS_STAR((SPItem *) items->data)) {
1556             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1557             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1558             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1559                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1560                                     + M_PI / (gint)adj->value));
1561             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1562             modmade = true;
1563         }
1564     }
1565     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1566                                    _("Star: Change number of corners"));
1568     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1571 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1573     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1575     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1576         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1577     }
1579     // quit if run by the attr_changed listener
1580     if (g_object_get_data( dataKludge, "freeze" )) {
1581         return;
1582     }
1584     // in turn, prevent listener from responding
1585     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1587     bool modmade = false;
1588     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1589     GSList const *items = selection->itemList();
1590     for (; items != NULL; items = items->next) {
1591         if (SP_IS_STAR((SPItem *) items->data)) {
1592             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1594             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1595             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1596             if (r2 < r1) {
1597                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1598             } else {
1599                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1600             }
1602             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1603             modmade = true;
1604         }
1605     }
1607     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1608                                    _("Star: Change spoke ratio"));
1610     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1613 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1615     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1616     bool flat = ege_select_one_action_get_active( act ) == 0;
1618     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1619         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1620                                     flat ? "true" : "false" );
1621     }
1623     // quit if run by the attr_changed listener
1624     if (g_object_get_data( dataKludge, "freeze" )) {
1625         return;
1626     }
1628     // in turn, prevent listener from responding
1629     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1631     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1632     GSList const *items = selection->itemList();
1633     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1634     bool modmade = false;
1636     if ( prop_action ) {
1637         gtk_action_set_sensitive( prop_action, !flat );
1638     }
1640     for (; items != NULL; items = items->next) {
1641         if (SP_IS_STAR((SPItem *) items->data)) {
1642             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1643             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1644             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1645             modmade = true;
1646         }
1647     }
1649     if (modmade) {
1650         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1651                          flat ? _("Make polygon") : _("Make star"));
1652     }
1654     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1657 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1659     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1661     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1662         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1663     }
1665     // quit if run by the attr_changed listener
1666     if (g_object_get_data( dataKludge, "freeze" )) {
1667         return;
1668     }
1670     // in turn, prevent listener from responding
1671     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1673     bool modmade = false;
1675     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1676     GSList const *items = selection->itemList();
1677     for (; items != NULL; items = items->next) {
1678         if (SP_IS_STAR((SPItem *) items->data)) {
1679             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1680             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1681             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1682             modmade = true;
1683         }
1684     }
1685     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1686                                    _("Star: Change rounding"));
1688     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1691 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1693     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1695     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1696         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1697     }
1699     // quit if run by the attr_changed listener
1700     if (g_object_get_data( dataKludge, "freeze" )) {
1701         return;
1702     }
1704     // in turn, prevent listener from responding
1705     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1707     bool modmade = false;
1709     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1710     GSList const *items = selection->itemList();
1711     for (; items != NULL; items = items->next) {
1712         if (SP_IS_STAR((SPItem *) items->data)) {
1713             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1714             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1715             SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1716             modmade = true;
1717         }
1718     }
1719     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1720                                    _("Star: Change randomization"));
1722     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1726 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1727                                        gchar const */*old_value*/, gchar const */*new_value*/,
1728                                        bool /*is_interactive*/, gpointer data)
1730     GtkWidget *tbl = GTK_WIDGET(data);
1732     // quit if run by the _changed callbacks
1733     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1734         return;
1735     }
1737     // in turn, prevent callbacks from responding
1738     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1740     GtkAdjustment *adj = 0;
1742     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1743     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1745     if (!strcmp(name, "inkscape:randomized")) {
1746         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1747         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1748     } else if (!strcmp(name, "inkscape:rounded")) {
1749         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1750         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1751     } else if (!strcmp(name, "inkscape:flatsided")) {
1752         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1753         char const *flatsides = repr->attribute("inkscape:flatsided");
1754         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1755         if ( flatsides && !strcmp(flatsides,"false") ) {
1756             ege_select_one_action_set_active( flat_action, 1 );
1757             gtk_action_set_sensitive( prop_action, TRUE );
1758         } else {
1759             ege_select_one_action_set_active( flat_action, 0 );
1760             gtk_action_set_sensitive( prop_action, FALSE );
1761         }
1762     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1763         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1764         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1765         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1766         if (r2 < r1) {
1767             gtk_adjustment_set_value(adj, r2/r1);
1768         } else {
1769             gtk_adjustment_set_value(adj, r1/r2);
1770         }
1771     } else if (!strcmp(name, "sodipodi:sides")) {
1772         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1773         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1774     }
1776     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1780 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1782     NULL, /* child_added */
1783     NULL, /* child_removed */
1784     star_tb_event_attr_changed,
1785     NULL, /* content_changed */
1786     NULL  /* order_changed */
1787 };
1790 /**
1791  *  \param selection Should not be NULL.
1792  */
1793 static void
1794 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1796     int n_selected = 0;
1797     Inkscape::XML::Node *repr = NULL;
1799     purge_repr_listener( tbl, tbl );
1801     for (GSList const *items = selection->itemList();
1802          items != NULL;
1803          items = items->next)
1804     {
1805         if (SP_IS_STAR((SPItem *) items->data)) {
1806             n_selected++;
1807             repr = SP_OBJECT_REPR((SPItem *) items->data);
1808         }
1809     }
1811     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1813     if (n_selected == 0) {
1814         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1815     } else if (n_selected == 1) {
1816         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1818         if (repr) {
1819             g_object_set_data( tbl, "repr", repr );
1820             Inkscape::GC::anchor(repr);
1821             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1822             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1823         }
1824     } else {
1825         // FIXME: implement averaging of all parameters for multiple selected stars
1826         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1827         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1828     }
1832 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1834     // FIXME: in this and all other _default functions, set some flag telling the value_changed
1835     // callbacks to lump all the changes for all selected objects in one undo step
1837     GtkAdjustment *adj = 0;
1839     // fixme: make settable in prefs!
1840     gint mag = 5;
1841     gdouble prop = 0.5;
1842     gboolean flat = FALSE;
1843     gdouble randomized = 0;
1844     gdouble rounded = 0;
1846     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1847     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1849     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1850     gtk_action_set_sensitive( sb2, !flat );
1852     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1853     gtk_adjustment_set_value(adj, mag);
1854     gtk_adjustment_value_changed(adj);
1856     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1857     gtk_adjustment_set_value(adj, prop);
1858     gtk_adjustment_value_changed(adj);
1860     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1861     gtk_adjustment_set_value(adj, rounded);
1862     gtk_adjustment_value_changed(adj);
1864     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1865     gtk_adjustment_set_value(adj, randomized);
1866     gtk_adjustment_value_changed(adj);
1870 void
1871 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1873     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1874     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1875     GtkWidget *l = gtk_label_new(NULL);
1876     gtk_label_set_markup(GTK_LABEL(l), title);
1877     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1878     gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1879     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1883 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1885     {
1886         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1887         ege_output_action_set_use_markup( act, TRUE );
1888         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1889         g_object_set_data( holder, "mode_action", act );
1890     }
1892     {
1893         EgeAdjustmentAction* eact = 0;
1894         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1895         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1897         /* Flatsided checkbox */
1898         {
1899             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1901             GtkTreeIter iter;
1902             gtk_list_store_append( model, &iter );
1903             gtk_list_store_set( model, &iter,
1904                                 0, _("Polygon"),
1905                                 1, _("Regular polygon (with one handle) instead of a star"),
1906                                 2, "star_flat",
1907                                 -1 );
1909             gtk_list_store_append( model, &iter );
1910             gtk_list_store_set( model, &iter,
1911                                 0, _("Star"),
1912                                 1, _("Star instead of a regular polygon (with one handle)"),
1913                                 2, "star_angled",
1914                                 -1 );
1916             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
1917             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1918             g_object_set_data( holder, "flat_action", act );
1920             ege_select_one_action_set_appearance( act, "full" );
1921             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1922             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1923             ege_select_one_action_set_icon_column( act, 2 );
1924             ege_select_one_action_set_tooltip_column( act, 1  );
1926             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1927             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1928         }
1930         /* Magnitude */
1931         {
1932         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1933         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1934         eact = create_adjustment_action( "MagnitudeAction",
1935                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
1936                                          "tools.shapes.star", "magnitude", 3,
1937                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1938                                          3, 1024, 1, 5,
1939                                          labels, values, G_N_ELEMENTS(labels),
1940                                          sp_stb_magnitude_value_changed,
1941                                          1.0, 0 );
1942         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1943         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1944         }
1946         /* Spoke ratio */
1947         {
1948         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1949         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1950         eact = create_adjustment_action( "SpokeAction",
1951                                          _("Spoke ratio"), _("Spoke ratio:"),
1952                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1953                                          // Base radius is the same for the closest handle.
1954                                          _("Base radius to tip radius ratio"),
1955                                          "tools.shapes.star", "proportion", 0.5,
1956                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1957                                          0.01, 1.0, 0.01, 0.1,
1958                                          labels, values, G_N_ELEMENTS(labels),
1959                                          sp_stb_proportion_value_changed );
1960         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1961         g_object_set_data( holder, "prop_action", eact );
1962         }
1964         if ( !isFlatSided ) {
1965             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1966         } else {
1967             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1968         }
1970         /* Roundedness */
1971         {
1972         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1973         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1974         eact = create_adjustment_action( "RoundednessAction",
1975                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1976                                          "tools.shapes.star", "rounded", 0.0,
1977                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1978                                          -10.0, 10.0, 0.01, 0.1,
1979                                          labels, values, G_N_ELEMENTS(labels),
1980                                          sp_stb_rounded_value_changed );
1981         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1982         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1983         }
1985         /* Randomization */
1986         {
1987         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1988         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1989         eact = create_adjustment_action( "RandomizationAction",
1990                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
1991                                          "tools.shapes.star", "randomized", 0.0,
1992                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1993                                          -10.0, 10.0, 0.001, 0.01,
1994                                          labels, values, G_N_ELEMENTS(labels),
1995                                          sp_stb_randomized_value_changed, 0.1, 3 );
1996         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1997         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1998         }
1999     }
2001     {
2002         /* Reset */
2003         {
2004             GtkAction* act = gtk_action_new( "StarResetAction",
2005                                              _("Defaults"),
2006                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2007                                              GTK_STOCK_CLEAR );
2008             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2009             gtk_action_group_add_action( mainActions, act );
2010             gtk_action_set_sensitive( act, TRUE );
2011         }
2012     }
2014     sigc::connection *connection = new sigc::connection(
2015         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2016         );
2017     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2018     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2022 //########################
2023 //##       Rect         ##
2024 //########################
2026 static void sp_rtb_sensitivize( GObject *tbl )
2028     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2029     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2030     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2032     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2033         gtk_action_set_sensitive( not_rounded, FALSE );
2034     } else {
2035         gtk_action_set_sensitive( not_rounded, TRUE );
2036     }
2040 static void
2041 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2042                           void (*setter)(SPRect *, gdouble))
2044     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2046     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2047     SPUnit const *unit = tracker->getActiveUnit();
2049     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2050         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2051     }
2053     // quit if run by the attr_changed listener
2054     if (g_object_get_data( tbl, "freeze" )) {
2055         return;
2056     }
2058     // in turn, prevent listener from responding
2059     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2061     bool modmade = false;
2062     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2063     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2064         if (SP_IS_RECT(items->data)) {
2065             if (adj->value != 0) {
2066                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2067             } else {
2068                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2069             }
2070             modmade = true;
2071         }
2072     }
2074     sp_rtb_sensitivize( tbl );
2076     if (modmade) {
2077         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2078                                    _("Change rectangle"));
2079     }
2081     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2084 static void
2085 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2087     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2090 static void
2091 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2093     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2096 static void
2097 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2099     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2102 static void
2103 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2105     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2110 static void
2111 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2113     GtkAdjustment *adj = 0;
2115     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2116     gtk_adjustment_set_value(adj, 0.0);
2117     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2118     gtk_adjustment_value_changed(adj);
2120     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2121     gtk_adjustment_set_value(adj, 0.0);
2122     gtk_adjustment_value_changed(adj);
2124     sp_rtb_sensitivize( obj );
2127 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2128                                        gchar const */*old_value*/, gchar const */*new_value*/,
2129                                        bool /*is_interactive*/, gpointer data)
2131     GObject *tbl = G_OBJECT(data);
2133     // quit if run by the _changed callbacks
2134     if (g_object_get_data( tbl, "freeze" )) {
2135         return;
2136     }
2138     // in turn, prevent callbacks from responding
2139     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2141     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2142     SPUnit const *unit = tracker->getActiveUnit();
2144     gpointer item = g_object_get_data( tbl, "item" );
2145     if (item && SP_IS_RECT(item)) {
2146         {
2147             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2148             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2149             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2150         }
2152         {
2153             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2154             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2155             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2156         }
2158         {
2159             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2160             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2161             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2162         }
2164         {
2165             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2166             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2167             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2168         }
2169     }
2171     sp_rtb_sensitivize( tbl );
2173     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2177 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2178     NULL, /* child_added */
2179     NULL, /* child_removed */
2180     rect_tb_event_attr_changed,
2181     NULL, /* content_changed */
2182     NULL  /* order_changed */
2183 };
2185 /**
2186  *  \param selection should not be NULL.
2187  */
2188 static void
2189 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2191     int n_selected = 0;
2192     Inkscape::XML::Node *repr = NULL;
2193     SPItem *item = NULL;
2195     if ( g_object_get_data( tbl, "repr" ) ) {
2196         g_object_set_data( tbl, "item", NULL );
2197     }
2198     purge_repr_listener( tbl, tbl );
2200     for (GSList const *items = selection->itemList();
2201          items != NULL;
2202          items = items->next) {
2203         if (SP_IS_RECT((SPItem *) items->data)) {
2204             n_selected++;
2205             item = (SPItem *) items->data;
2206             repr = SP_OBJECT_REPR(item);
2207         }
2208     }
2210     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2212     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2214     if (n_selected == 0) {
2215         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2217         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2218         gtk_action_set_sensitive(w, FALSE);
2219         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2220         gtk_action_set_sensitive(h, FALSE);
2222     } else if (n_selected == 1) {
2223         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2224         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2226         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2227         gtk_action_set_sensitive(w, TRUE);
2228         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2229         gtk_action_set_sensitive(h, TRUE);
2231         if (repr) {
2232             g_object_set_data( tbl, "repr", repr );
2233             g_object_set_data( tbl, "item", item );
2234             Inkscape::GC::anchor(repr);
2235             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2236             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2237         }
2238     } else {
2239         // FIXME: implement averaging of all parameters for multiple selected
2240         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2241         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2242         sp_rtb_sensitivize( tbl );
2243     }
2247 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2249     EgeAdjustmentAction* eact = 0;
2251     {
2252         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2253         ege_output_action_set_use_markup( act, TRUE );
2254         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2255         g_object_set_data( holder, "mode_action", act );
2256     }
2258     // rx/ry units menu: create
2259     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2260     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2261     // fixme: add % meaning per cent of the width/height
2262     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2263     g_object_set_data( holder, "tracker", tracker );
2265     /* W */
2266     {
2267         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2268         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2269         eact = create_adjustment_action( "RectWidthAction",
2270                                          _("Width"), _("W:"), _("Width of rectangle"),
2271                                          "tools.shapes.rect", "width", 0,
2272                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2273                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2274                                          labels, values, G_N_ELEMENTS(labels),
2275                                          sp_rtb_width_value_changed );
2276         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2277         g_object_set_data( holder, "width_action", eact );
2278         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2279         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2280     }
2282     /* H */
2283     {
2284         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2285         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2286         eact = create_adjustment_action( "RectHeightAction",
2287                                          _("Height"), _("H:"), _("Height of rectangle"),
2288                                          "tools.shapes.rect", "height", 0,
2289                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2290                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2291                                          labels, values, G_N_ELEMENTS(labels),
2292                                          sp_rtb_height_value_changed );
2293         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2294         g_object_set_data( holder, "height_action", eact );
2295         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2296         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2297     }
2299     /* rx */
2300     {
2301         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2302         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2303         eact = create_adjustment_action( "RadiusXAction",
2304                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2305                                          "tools.shapes.rect", "rx", 0,
2306                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2307                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2308                                          labels, values, G_N_ELEMENTS(labels),
2309                                          sp_rtb_rx_value_changed);
2310         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2311         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2312     }
2314     /* ry */
2315     {
2316         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2317         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2318         eact = create_adjustment_action( "RadiusYAction",
2319                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2320                                          "tools.shapes.rect", "ry", 0,
2321                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2322                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2323                                          labels, values, G_N_ELEMENTS(labels),
2324                                          sp_rtb_ry_value_changed);
2325         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2326         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2327     }
2329     // add the units menu
2330     {
2331         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2332         gtk_action_group_add_action( mainActions, act );
2333     }
2335     /* Reset */
2336     {
2337         InkAction* inky = ink_action_new( "RectResetAction",
2338                                           _("Not rounded"),
2339                                           _("Make corners sharp"),
2340                                           "squared_corner",
2341                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2342         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2343         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2344         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2345         g_object_set_data( holder, "not_rounded", inky );
2346     }
2348     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2349     sp_rtb_sensitivize( holder );
2351     sigc::connection *connection = new sigc::connection(
2352         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2353         );
2354     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2355     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2358 //########################
2359 //##       3D Box       ##
2360 //########################
2362 // normalize angle so that it lies in the interval [0,360]
2363 static double box3d_normalize_angle (double a) {
2364     double angle = a + ((int) (a/360.0))*360;
2365     if (angle < 0) {
2366         angle += 360.0;
2367     }
2368     return angle;
2371 static void
2372 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2373                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2374     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2375     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2376     //       are reset).
2377     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2379     if (is_infinite) {
2380         gtk_toggle_action_set_active(tact, TRUE);
2381         gtk_action_set_sensitive(act, TRUE);
2383         double angle = persp3d_get_infinite_angle(persp, axis);
2384         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2385             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2386         }
2387     } else {
2388         gtk_toggle_action_set_active(tact, FALSE);
2389         gtk_action_set_sensitive(act, FALSE);
2390     }
2393 static void
2394 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2395     if (!persp_repr) {
2396         g_print ("No perspective given to box3d_resync_toolbar().\n");
2397         return;
2398     }
2400     GtkWidget *tbl = GTK_WIDGET(data);
2401     GtkAdjustment *adj = 0;
2402     GtkAction *act = 0;
2403     GtkToggleAction *tact = 0;
2404     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2405     {
2406         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2407         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2408         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2410         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2411     }
2412     {
2413         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2414         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2415         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2417         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2418     }
2419     {
2420         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2421         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2422         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2424         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2425     }
2428 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2429                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2430                                                   bool /*is_interactive*/, gpointer data)
2432     GtkWidget *tbl = GTK_WIDGET(data);
2434     // quit if run by the attr_changed listener
2435     // note: it used to work without the differently called freeze_ attributes (here and in
2436     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2437     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2438         return;
2439     }
2441     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2442     // sp_document_maybe_done() when the document is undo insensitive)
2443     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2445     // TODO: Only update the appropriate part of the toolbar
2446 //    if (!strcmp(name, "inkscape:vp_z")) {
2447         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2448 //    }
2450     Persp3D *persp = persp3d_get_from_repr(repr);
2451     persp3d_update_box_reprs(persp);
2453     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2456 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2458     NULL, /* child_added */
2459     NULL, /* child_removed */
2460     box3d_persp_tb_event_attr_changed,
2461     NULL, /* content_changed */
2462     NULL  /* order_changed */
2463 };
2465 /**
2466  *  \param selection Should not be NULL.
2467  */
2468 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2469 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2470 static void
2471 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2473     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2474     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2475     // update the perspectives with infinite VPs and leave the other ones untouched).
2477     Inkscape::XML::Node *persp_repr = NULL;
2478     purge_repr_listener(tbl, tbl);
2480     SPItem *item = selection->singleItem();
2481     if (item && SP_IS_BOX3D(item)) {
2482         // FIXME: Also deal with multiple selected boxes
2483         SPBox3D *box = SP_BOX3D(item);
2484         Persp3D *persp = box3d_get_perspective(box);
2485         persp_repr = SP_OBJECT_REPR(persp);
2486         if (persp_repr) {
2487             g_object_set_data(tbl, "repr", persp_repr);
2488             Inkscape::GC::anchor(persp_repr);
2489             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2490             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2491         }
2493         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2494         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2496         box3d_resync_toolbar(persp_repr, tbl);
2497     }
2500 static void
2501 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2503     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2504     SPDocument *document = sp_desktop_document(desktop);
2506     // quit if run by the attr_changed listener
2507     // note: it used to work without the differently called freeze_ attributes (here and in
2508     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2509     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2510         return;
2511     }
2513     // in turn, prevent listener from responding
2514     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2516     //Persp3D *persp = document->current_persp3d;
2517     std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps();
2518     if (sel_persps.empty()) {
2519         // this can happen when the document is created; we silently ignore it
2520         return;
2521     }
2522     Persp3D *persp = *(sel_persps.begin());
2524     persp->tmat.set_infinite_direction (axis, adj->value);
2525     SP_OBJECT(persp)->updateRepr();
2527     // TODO: use the correct axis here, too
2528     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2530     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2534 static void
2535 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2537     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2540 static void
2541 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2543     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2546 static void
2547 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2549     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2553 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2555     // TODO: Take all selected perspectives into account
2556     std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps();
2557     if (sel_persps.empty()) {
2558         // this can happen when the document is created; we silently ignore it
2559         return;
2560     }
2561     Persp3D *persp = *(sel_persps.begin());
2563     bool set_infinite = gtk_toggle_action_get_active(act);
2564     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2567 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2569     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2572 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2574     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2577 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2579     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2582 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2584     EgeAdjustmentAction* eact = 0;
2585     SPDocument *document = sp_desktop_document (desktop);
2586     Persp3D *persp = document->current_persp3d;
2588     EgeAdjustmentAction* box3d_angle_x = 0;
2589     EgeAdjustmentAction* box3d_angle_y = 0;
2590     EgeAdjustmentAction* box3d_angle_z = 0;
2592     /* Angle X */
2593     {
2594         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2595         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2596         eact = create_adjustment_action( "3DBoxAngleXAction",
2597                                          _("Angle in X direction"), _("Angle X:"),
2598                                          // Translators: PL is short for 'perspective line'
2599                                          _("Angle of PLs in X direction"),
2600                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2601                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2602                                          -360.0, 360.0, 1.0, 10.0,
2603                                          labels, values, G_N_ELEMENTS(labels),
2604                                          box3d_angle_x_value_changed );
2605         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2606         g_object_set_data( holder, "box3d_angle_x_action", eact );
2607         box3d_angle_x = eact;
2608     }
2610     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2611         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2612     } else {
2613         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2614     }
2617     /* VP X state */
2618     {
2619         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2620                                                       // Translators: VP is short for 'vanishing point'
2621                                                       _("State of VP in X direction"),
2622                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2623                                                       "toggle_vp_x",
2624                                                       Inkscape::ICON_SIZE_DECORATION );
2625         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2626         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2627         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2628         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2629         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2630     }
2632     /* Angle Y */
2633     {
2634         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2635         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2636         eact = create_adjustment_action( "3DBoxAngleYAction",
2637                                          _("Angle in Y direction"), _("Angle Y:"),
2638                                          // Translators: PL is short for 'perspective line'
2639                                          _("Angle of PLs in Y direction"),
2640                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2641                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2642                                          -360.0, 360.0, 1.0, 10.0,
2643                                          labels, values, G_N_ELEMENTS(labels),
2644                                          box3d_angle_y_value_changed );
2645         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2646         g_object_set_data( holder, "box3d_angle_y_action", eact );
2647         box3d_angle_y = eact;
2648     }
2650     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2651         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2652     } else {
2653         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2654     }
2656     /* VP Y state */
2657     {
2658         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2659                                                       // Translators: VP is short for 'vanishing point'
2660                                                       _("State of VP in Y direction"),
2661                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2662                                                       "toggle_vp_y",
2663                                                       Inkscape::ICON_SIZE_DECORATION );
2664         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2665         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2666         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2667         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2668         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2669     }
2671     /* Angle Z */
2672     {
2673         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2674         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2675         eact = create_adjustment_action( "3DBoxAngleZAction",
2676                                          _("Angle in Z direction"), _("Angle Z:"),
2677                                          // Translators: PL is short for 'perspective line'
2678                                          _("Angle of PLs in Z direction"),
2679                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
2680                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2681                                          -360.0, 360.0, 1.0, 10.0,
2682                                          labels, values, G_N_ELEMENTS(labels),
2683                                          box3d_angle_z_value_changed );
2684         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2685         g_object_set_data( holder, "box3d_angle_z_action", eact );
2686         box3d_angle_z = eact;
2687     }
2689     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2690         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2691     } else {
2692         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2693     }
2695     /* VP Z state */
2696     {
2697         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2698                                                       // Translators: VP is short for 'vanishing point'
2699                                                       _("State of VP in Z direction"),
2700                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2701                                                       "toggle_vp_z",
2702                                                       Inkscape::ICON_SIZE_DECORATION );
2703         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2704         g_object_set_data( holder, "box3d_vp_z_state_action", act );
2705         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2706         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2707         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2708     }
2710     sigc::connection *connection = new sigc::connection(
2711         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2712        );
2713     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2714     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2717 //########################
2718 //##       Spiral       ##
2719 //########################
2721 static void
2722 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2724     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2726     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2727         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2728     }
2730     // quit if run by the attr_changed listener
2731     if (g_object_get_data( tbl, "freeze" )) {
2732         return;
2733     }
2735     // in turn, prevent listener from responding
2736     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2738     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2740     bool modmade = false;
2741     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2742          items != NULL;
2743          items = items->next)
2744     {
2745         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2746             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2747             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2748             SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2749             modmade = true;
2750         }
2751     }
2753     g_free(namespaced_name);
2755     if (modmade) {
2756         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2757                                    _("Change spiral"));
2758     }
2760     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2763 static void
2764 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2766     sp_spl_tb_value_changed(adj, tbl, "revolution");
2769 static void
2770 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2772     sp_spl_tb_value_changed(adj, tbl, "expansion");
2775 static void
2776 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2778     sp_spl_tb_value_changed(adj, tbl, "t0");
2781 static void
2782 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2784     GtkWidget *tbl = GTK_WIDGET(obj);
2786     GtkAdjustment *adj;
2788     // fixme: make settable
2789     gdouble rev = 5;
2790     gdouble exp = 1.0;
2791     gdouble t0 = 0.0;
2793     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2794     gtk_adjustment_set_value(adj, rev);
2795     gtk_adjustment_value_changed(adj);
2797     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2798     gtk_adjustment_set_value(adj, exp);
2799     gtk_adjustment_value_changed(adj);
2801     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2802     gtk_adjustment_set_value(adj, t0);
2803     gtk_adjustment_value_changed(adj);
2805     spinbutton_defocus(GTK_OBJECT(tbl));
2809 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2810                                          gchar const */*old_value*/, gchar const */*new_value*/,
2811                                          bool /*is_interactive*/, gpointer data)
2813     GtkWidget *tbl = GTK_WIDGET(data);
2815     // quit if run by the _changed callbacks
2816     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2817         return;
2818     }
2820     // in turn, prevent callbacks from responding
2821     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2823     GtkAdjustment *adj;
2824     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2825     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2827     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2828     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2830     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2831     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2833     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2837 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2838     NULL, /* child_added */
2839     NULL, /* child_removed */
2840     spiral_tb_event_attr_changed,
2841     NULL, /* content_changed */
2842     NULL  /* order_changed */
2843 };
2845 static void
2846 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2848     int n_selected = 0;
2849     Inkscape::XML::Node *repr = NULL;
2851     purge_repr_listener( tbl, tbl );
2853     for (GSList const *items = selection->itemList();
2854          items != NULL;
2855          items = items->next)
2856     {
2857         if (SP_IS_SPIRAL((SPItem *) items->data)) {
2858             n_selected++;
2859             repr = SP_OBJECT_REPR((SPItem *) items->data);
2860         }
2861     }
2863     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2865     if (n_selected == 0) {
2866         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2867     } else if (n_selected == 1) {
2868         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2870         if (repr) {
2871             g_object_set_data( tbl, "repr", repr );
2872             Inkscape::GC::anchor(repr);
2873             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2874             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2875         }
2876     } else {
2877         // FIXME: implement averaging of all parameters for multiple selected
2878         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2879         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2880     }
2884 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2886     EgeAdjustmentAction* eact = 0;
2888     {
2889         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2890         ege_output_action_set_use_markup( act, TRUE );
2891         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2892         g_object_set_data( holder, "mode_action", act );
2893     }
2895     /* Revolution */
2896     {
2897         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2898         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2899         eact = create_adjustment_action( "SpiralRevolutionAction",
2900                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
2901                                          "tools.shapes.spiral", "revolution", 3.0,
2902                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2903                                          0.01, 1024.0, 0.1, 1.0,
2904                                          labels, values, G_N_ELEMENTS(labels),
2905                                          sp_spl_tb_revolution_value_changed, 1, 2);
2906         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2907     }
2909     /* Expansion */
2910     {
2911         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2912         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2913         eact = create_adjustment_action( "SpiralExpansionAction",
2914                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2915                                          "tools.shapes.spiral", "expansion", 1.0,
2916                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2917                                          0.0, 1000.0, 0.01, 1.0,
2918                                          labels, values, G_N_ELEMENTS(labels),
2919                                          sp_spl_tb_expansion_value_changed);
2920         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2921     }
2923     /* T0 */
2924     {
2925         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2926         gdouble values[] = {0, 0.5, 0.9};
2927         eact = create_adjustment_action( "SpiralT0Action",
2928                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2929                                          "tools.shapes.spiral", "t0", 0.0,
2930                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2931                                          0.0, 0.999, 0.01, 1.0,
2932                                          labels, values, G_N_ELEMENTS(labels),
2933                                          sp_spl_tb_t0_value_changed);
2934         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2935     }
2937     /* Reset */
2938     {
2939         InkAction* inky = ink_action_new( "SpiralResetAction",
2940                                           _("Defaults"),
2941                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2942                                           GTK_STOCK_CLEAR,
2943                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2944         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2945         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2946     }
2949     sigc::connection *connection = new sigc::connection(
2950         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2951         );
2952     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2953     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2956 //########################
2957 //##     Pen/Pencil    ##
2958 //########################
2961 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2963     // Put stuff here
2966 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2968     // Put stuff here
2971 //########################
2972 //##       Tweak        ##
2973 //########################
2975 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2977     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2980 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2982     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2985 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
2987     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2990 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2992     int mode = ege_select_one_action_get_active( act );
2993     prefs_set_int_attribute("tools.tweak", "mode", mode);
2995     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2996     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2997     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2998     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2999     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3000     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3001     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3002         if (doh) gtk_action_set_sensitive (doh, TRUE);
3003         if (dos) gtk_action_set_sensitive (dos, TRUE);
3004         if (dol) gtk_action_set_sensitive (dol, TRUE);
3005         if (doo) gtk_action_set_sensitive (doo, TRUE);
3006         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3007         if (fid) gtk_action_set_sensitive (fid, FALSE);
3008     } else {
3009         if (doh) gtk_action_set_sensitive (doh, FALSE);
3010         if (dos) gtk_action_set_sensitive (dos, FALSE);
3011         if (dol) gtk_action_set_sensitive (dol, FALSE);
3012         if (doo) gtk_action_set_sensitive (doo, FALSE);
3013         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3014         if (fid) gtk_action_set_sensitive (fid, TRUE);
3015     }
3018 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3020     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3023 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3024     bool show = gtk_toggle_action_get_active( act );
3025     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3027 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3028     bool show = gtk_toggle_action_get_active( act );
3029     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3031 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3032     bool show = gtk_toggle_action_get_active( act );
3033     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3035 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3036     bool show = gtk_toggle_action_get_active( act );
3037     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3040 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3042     {
3043         /* Width */
3044         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3045         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3046         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3047                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3048                                                               "tools.tweak", "width", 15,
3049                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3050                                                               1, 100, 1.0, 10.0,
3051                                                               labels, values, G_N_ELEMENTS(labels),
3052                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3053         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3054         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3055     }
3058     {
3059         /* Force */
3060         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3061         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3062         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3063                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3064                                                               "tools.tweak", "force", 20,
3065                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3066                                                               1, 100, 1.0, 10.0,
3067                                                               labels, values, G_N_ELEMENTS(labels),
3068                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3069         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3070         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3071     }
3073     /* Mode */
3074     {
3075         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3077         GtkTreeIter iter;
3078         gtk_list_store_append( model, &iter );
3079         gtk_list_store_set( model, &iter,
3080                             0, _("Push mode"),
3081                             1, _("Push parts of paths in any direction"),
3082                             2, "tweak_push_mode",
3083                             -1 );
3085         gtk_list_store_append( model, &iter );
3086         gtk_list_store_set( model, &iter,
3087                             0, _("Shrink mode"),
3088                             1, _("Shrink (inset) parts of paths"),
3089                             2, "tweak_shrink_mode",
3090                             -1 );
3092         gtk_list_store_append( model, &iter );
3093         gtk_list_store_set( model, &iter,
3094                             0, _("Grow mode"),
3095                             1, _("Grow (outset) parts of paths"),
3096                             2, "tweak_grow_mode",
3097                             -1 );
3099         gtk_list_store_append( model, &iter );
3100         gtk_list_store_set( model, &iter,
3101                             0, _("Attract mode"),
3102                             1, _("Attract parts of paths towards cursor"),
3103                             2, "tweak_attract_mode",
3104                             -1 );
3106         gtk_list_store_append( model, &iter );
3107         gtk_list_store_set( model, &iter,
3108                             0, _("Repel mode"),
3109                             1, _("Repel parts of paths from cursor"),
3110                             2, "tweak_repel_mode",
3111                             -1 );
3113         gtk_list_store_append( model, &iter );
3114         gtk_list_store_set( model, &iter,
3115                             0, _("Roughen mode"),
3116                             1, _("Roughen parts of paths"),
3117                             2, "tweak_roughen_mode",
3118                             -1 );
3120         gtk_list_store_append( model, &iter );
3121         gtk_list_store_set( model, &iter,
3122                             0, _("Color paint mode"),
3123                             1, _("Paint the tool's color upon selected objects"),
3124                             2, "tweak_colorpaint_mode",
3125                             -1 );
3127         gtk_list_store_append( model, &iter );
3128         gtk_list_store_set( model, &iter,
3129                             0, _("Color jitter mode"),
3130                             1, _("Jitter the colors of selected objects"),
3131                             2, "tweak_colorjitter_mode",
3132                             -1 );
3134         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3135         g_object_set( act, "short_label", _("Mode:"), NULL );
3136         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3137         g_object_set_data( holder, "mode_action", act );
3139         ege_select_one_action_set_appearance( act, "full" );
3140         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3141         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3142         ege_select_one_action_set_icon_column( act, 2 );
3143         ege_select_one_action_set_tooltip_column( act, 1  );
3145         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3146         ege_select_one_action_set_active( act, mode );
3147         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3149         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3150     }
3152     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3154     {
3155         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3156         ege_output_action_set_use_markup( act, TRUE );
3157         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3158         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3159             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3160         g_object_set_data( holder, "tweak_channels_label", act);
3161     }
3163     {
3164         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3165                                                       _("Hue"),
3166                                                       _("In color mode, act on objects' hue"),
3167                                                       NULL,
3168                                                       Inkscape::ICON_SIZE_DECORATION );
3169         g_object_set( act, "short_label", _("H"), NULL );
3170         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3171         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3172         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3173         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3174             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3175         g_object_set_data( holder, "tweak_doh", act);
3176     }
3177     {
3178         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3179                                                       _("Saturation"),
3180                                                       _("In color mode, act on objects' saturation"),
3181                                                       NULL,
3182                                                       Inkscape::ICON_SIZE_DECORATION );
3183         g_object_set( act, "short_label", _("S"), NULL );
3184         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3185         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3186         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3187         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3188             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3189         g_object_set_data( holder, "tweak_dos", act );
3190     }
3191     {
3192         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3193                                                       _("Lightness"),
3194                                                       _("In color mode, act on objects' lightness"),
3195                                                       NULL,
3196                                                       Inkscape::ICON_SIZE_DECORATION );
3197         g_object_set( act, "short_label", _("L"), NULL );
3198         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3199         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3200         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3201         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3202             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3203         g_object_set_data( holder, "tweak_dol", act );
3204     }
3205     {
3206         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3207                                                       _("Opacity"),
3208                                                       _("In color mode, act on objects' opacity"),
3209                                                       NULL,
3210                                                       Inkscape::ICON_SIZE_DECORATION );
3211         g_object_set( act, "short_label", _("O"), NULL );
3212         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3213         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3214         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3215         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3216             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3217         g_object_set_data( holder, "tweak_doo", act );
3218     }
3220     {   /* Fidelity */
3221         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3222         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3223         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3224                                                               _("Fidelity"), _("Fidelity:"),
3225                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3226                                                               "tools.tweak", "fidelity", 50,
3227                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3228                                                               1, 100, 1.0, 10.0,
3229                                                               labels, values, G_N_ELEMENTS(labels),
3230                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3231         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3232         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3233         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3234             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3235         g_object_set_data( holder, "tweak_fidelity", eact );
3236     }
3239     /* Use Pressure button */
3240     {
3241         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3242                                                       _("Pressure"),
3243                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3244                                                       "use_pressure",
3245                                                       Inkscape::ICON_SIZE_DECORATION );
3246         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3247         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3248         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3249     }
3254 //########################
3255 //##     Calligraphy    ##
3256 //########################
3258 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3260     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3263 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3265     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3268 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3270     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3273 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3275     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3278 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3280     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3283 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3285     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3288 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3290     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3293 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3295     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3298 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3300     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3303 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3305     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3308 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3310     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3312     gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3315 struct KeyValue {
3316     char const *key;
3317     double value;
3318 };
3322 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3323     struct ProfileElement {
3324         char const *name;
3325         double def;
3326         double min;
3327         double max;
3328     };
3329     ProfileElement profile[] = {
3330         {"mass",0.02, 0.0, 1.0},
3331         {"wiggle",0.0, 0.0, 1.0},
3332         {"angle",30.0, -90.0, 90.0},
3333         {"width",15.0, 1.0, 100.0},
3334         {"thinning",0.1, -1.0, 1.0},
3335         {"tremor",0.0, 0.0, 1.0},
3336         {"flatness",0.9, 0.0, 1.0},
3337         {"cap_rounding",0.0, 0.0, 5.0}
3338     };
3339     
3340     gint preset_index = ege_select_one_action_get_active( act );   
3341     gchar *pref_path = g_strdup_printf("tools.calligraphic.preset.cp%d", preset_index); 
3342     for (unsigned i = 0; i < 8; ++i) {
3343         ProfileElement const &pe = profile[i];
3344         double value = prefs_get_double_attribute_limited(pref_path,pe.name, pe.def, pe.min, pe.max);
3345         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3346         if ( adj ) {
3347             gtk_adjustment_set_value(adj, value);
3348         }
3349     } 
3350     free(pref_path);
3355 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3357     {
3358         EgeAdjustmentAction* calligraphy_angle = 0;
3360         {
3361         /* Width */
3362         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3363         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3364         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3365                                                               _("Pen Width"), _("Width:"),
3366                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
3367                                                               "tools.calligraphic", "width", 15,
3368                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3369                                                               1, 100, 1.0, 10.0,
3370                                                               labels, values, G_N_ELEMENTS(labels),
3371                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
3372         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3373         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3374         }
3376         {
3377         /* Thinning */
3378             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3379             gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3380         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3381                                                               _("Stroke Thinning"), _("Thinning:"),
3382                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3383                                                               "tools.calligraphic", "thinning", 0.1,
3384                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3385                                                               -1.0, 1.0, 0.01, 0.1,
3386                                                               labels, values, G_N_ELEMENTS(labels),
3387                                                               sp_ddc_velthin_value_changed, 0.01, 2);
3388         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3389         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3390         }
3392         {
3393         /* Angle */
3394         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3395         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3396         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3397                                                               _("Pen Angle"), _("Angle:"),
3398                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3399                                                               "tools.calligraphic", "angle", 30,
3400                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3401                                                               -90.0, 90.0, 1.0, 10.0,
3402                                                               labels, values, G_N_ELEMENTS(labels),
3403                                                               sp_ddc_angle_value_changed, 1, 0 );
3404         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3405         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3406         calligraphy_angle = eact;
3407         }
3409         {
3410         /* Fixation */
3411             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3412         gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3413         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3414                                                               _("Fixation"), _("Fixation:"),
3415                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3416                                                               "tools.calligraphic", "flatness", 0.9,
3417                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3418                                                               0.0, 1.0, 0.01, 0.1,
3419                                                               labels, values, G_N_ELEMENTS(labels),
3420                                                               sp_ddc_flatness_value_changed, 0.01, 2 );
3421         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3422         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3423         }
3425         {
3426         /* Cap Rounding */
3427             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3428         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3429         // TRANSLATORS: "cap" means "end" (both start and finish) here
3430         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3431                                                               _("Cap rounding"), _("Caps:"),
3432                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3433                                                               "tools.calligraphic", "cap_rounding", 0.0,
3434                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3435                                                               0.0, 5.0, 0.01, 0.1,
3436                                                               labels, values, G_N_ELEMENTS(labels),
3437                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3438         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3439         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3440         }
3442         {
3443         /* Tremor */
3444             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3445         gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3446         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3447                                                               _("Stroke Tremor"), _("Tremor:"),
3448                                                               _("Increase to make strokes rugged and trembling"),
3449                                                               "tools.calligraphic", "tremor", 0.0,
3450                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3451                                                               0.0, 1.0, 0.01, 0.1,
3452                                                               labels, values, G_N_ELEMENTS(labels),
3453                                                               sp_ddc_tremor_value_changed, 0.01, 2 );
3455         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3456         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3457         }
3459         {
3460         /* Wiggle */
3461         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3462         gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3463         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3464                                                               _("Pen Wiggle"), _("Wiggle:"),
3465                                                               _("Increase to make the pen waver and wiggle"),
3466                                                               "tools.calligraphic", "wiggle", 0.0,
3467                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3468                                                               0.0, 1.0, 0.01, 0.1,
3469                                                               labels, values, G_N_ELEMENTS(labels),
3470                                                               sp_ddc_wiggle_value_changed, 0.01, 2 );
3471         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3472         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3473         }
3475         {
3476         /* Mass */
3477             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3478         gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3479         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3480                                                               _("Pen Mass"), _("Mass:"),
3481                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
3482                                                               "tools.calligraphic", "mass", 0.02,
3483                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3484                                                               0.0, 1.0, 0.01, 0.1,
3485                                                               labels, values, G_N_ELEMENTS(labels),
3486                                                               sp_ddc_mass_value_changed, 0.01, 2 );
3487         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3488         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3489         }
3492         /* Trace Background button */
3493         {
3494             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3495                                                           _("Trace Background"),
3496                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3497                                                           "trace_background",
3498                                                           Inkscape::ICON_SIZE_DECORATION );
3499             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3500             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3501             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3502         }
3504         /* Use Pressure button */
3505         {
3506             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3507                                                           _("Pressure"),
3508                                                           _("Use the pressure of the input device to alter the width of the pen"),
3509                                                           "use_pressure",
3510                                                           Inkscape::ICON_SIZE_DECORATION );
3511             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3512             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3513             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3514         }
3516         /* Use Tilt button */
3517         {
3518             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3519                                                           _("Tilt"),
3520                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
3521                                                           "use_tilt",
3522                                                           Inkscape::ICON_SIZE_DECORATION );
3523             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3524             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3525             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3526             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3527         }
3529         /*calligraphic profile */
3530         {
3531             GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );            
3532             gchar *pref_path;
3534             int max = prefs_get_int_attribute("tools.calligraphic.preset","max_presets",99);
3535             for ( int ii = 0; ii < max; ++ii){
3536                 pref_path = g_strdup_printf("tools.calligraphic.preset.cp%d", ii);
3538                 if ( ! pref_path_exists(pref_path)){
3539                     free(pref_path );
3540                     break;
3541                 }
3543                 gchar const *preset_name = prefs_get_string_attribute(pref_path,"name");
3544                 GtkTreeIter iter;
3545                 gtk_list_store_append( model, &iter );
3546                 gtk_list_store_set( model, &iter, 0, preset_name, 1, ii, -1 );
3547                 free(pref_path );
3548             }
3549             pref_path = NULL;
3550             EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", _("Profile"), ("Change calligraphic profile"), NULL, GTK_TREE_MODEL(model) );
3551             g_object_set( act1, "short_label", _("Profile:"), NULL );
3552             ege_select_one_action_set_appearance( act1, "compact" );
3553             g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
3554             gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3555             g_object_set_data( holder, "channels_action", act1 );
3556         }
3557     }
3561 //########################
3562 //##    Circle / Arc    ##
3563 //########################
3565 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3567     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3568     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3570     if (v1 == 0 && v2 == 0) {
3571         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3572             gtk_action_set_sensitive( ocb, FALSE );
3573             gtk_action_set_sensitive( make_whole, FALSE );
3574         }
3575     } else {
3576         gtk_action_set_sensitive( ocb, TRUE );
3577         gtk_action_set_sensitive( make_whole, TRUE );
3578     }
3581 static void
3582 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3584     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3586     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3587         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3588     }
3590     // quit if run by the attr_changed listener
3591     if (g_object_get_data( tbl, "freeze" )) {
3592         return;
3593     }
3595     // in turn, prevent listener from responding
3596     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3598     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3600     bool modmade = false;
3601     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3602          items != NULL;
3603          items = items->next)
3604     {
3605         SPItem *item = SP_ITEM(items->data);
3607         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3609             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3610             SPArc *arc = SP_ARC(item);
3612             if (!strcmp(value_name, "start"))
3613                 ge->start = (adj->value * M_PI)/ 180;
3614             else
3615                 ge->end = (adj->value * M_PI)/ 180;
3617             sp_genericellipse_normalize(ge);
3618             ((SPObject *)arc)->updateRepr();
3619             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3621             modmade = true;
3622         }
3623     }
3625     g_free(namespaced_name);
3627     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3629     sp_arctb_sensitivize( tbl, adj->value, other->value );
3631     if (modmade) {
3632         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3633                                    _("Arc: Change start/end"));
3634     }
3636     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3640 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
3642     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
3645 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3647     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
3650 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3652     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3653     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3654         if ( ege_select_one_action_get_active( act ) != 0 ) {
3655             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3656         } else {
3657             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3658         }
3659     }
3661     // quit if run by the attr_changed listener
3662     if (g_object_get_data( tbl, "freeze" )) {
3663         return;
3664     }
3666     // in turn, prevent listener from responding
3667     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3669     bool modmade = false;
3671     if ( ege_select_one_action_get_active(act) != 0 ) {
3672         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3673              items != NULL;
3674              items = items->next)
3675         {
3676             if (SP_IS_ARC((SPItem *) items->data)) {
3677                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3678                 repr->setAttribute("sodipodi:open", "true");
3679                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3680                 modmade = true;
3681             }
3682         }
3683     } else {
3684         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3685              items != NULL;
3686              items = items->next)
3687         {
3688             if (SP_IS_ARC((SPItem *) items->data))    {
3689                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3690                 repr->setAttribute("sodipodi:open", NULL);
3691                 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3692                 modmade = true;
3693             }
3694         }
3695     }
3697     if (modmade) {
3698         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3699                                    _("Arc: Change open/closed"));
3700     }
3702     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3705 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3707     GtkAdjustment *adj;
3708     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3709     gtk_adjustment_set_value(adj, 0.0);
3710     gtk_adjustment_value_changed(adj);
3712     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3713     gtk_adjustment_set_value(adj, 0.0);
3714     gtk_adjustment_value_changed(adj);
3716     spinbutton_defocus( GTK_OBJECT(obj) );
3719 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3720                                       gchar const */*old_value*/, gchar const */*new_value*/,
3721                                       bool /*is_interactive*/, gpointer data)
3723     GObject *tbl = G_OBJECT(data);
3725     // quit if run by the _changed callbacks
3726     if (g_object_get_data( tbl, "freeze" )) {
3727         return;
3728     }
3730     // in turn, prevent callbacks from responding
3731     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3733     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3734     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3736     GtkAdjustment *adj1,*adj2;
3737     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3738     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3739     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3740     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3742     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3744     char const *openstr = NULL;
3745     openstr = repr->attribute("sodipodi:open");
3746     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3748     if (openstr) {
3749         ege_select_one_action_set_active( ocb, 1 );
3750     } else {
3751         ege_select_one_action_set_active( ocb, 0 );
3752     }
3754     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3757 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3758     NULL, /* child_added */
3759     NULL, /* child_removed */
3760     arc_tb_event_attr_changed,
3761     NULL, /* content_changed */
3762     NULL  /* order_changed */
3763 };
3766 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3768     int n_selected = 0;
3769     Inkscape::XML::Node *repr = NULL;
3771     purge_repr_listener( tbl, tbl );
3773     for (GSList const *items = selection->itemList();
3774          items != NULL;
3775          items = items->next)
3776     {
3777         if (SP_IS_ARC((SPItem *) items->data)) {
3778             n_selected++;
3779             repr = SP_OBJECT_REPR((SPItem *) items->data);
3780         }
3781     }
3783     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3785     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3786     if (n_selected == 0) {
3787         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3788     } else if (n_selected == 1) {
3789         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3790         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3792         if (repr) {
3793             g_object_set_data( tbl, "repr", repr );
3794             Inkscape::GC::anchor(repr);
3795             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3796             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3797         }
3798     } else {
3799         // FIXME: implement averaging of all parameters for multiple selected
3800         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3801         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3802         sp_arctb_sensitivize( tbl, 1, 0 );
3803     }
3807 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3809     EgeAdjustmentAction* eact = 0;
3812     {
3813         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3814         ege_output_action_set_use_markup( act, TRUE );
3815         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3816         g_object_set_data( holder, "mode_action", act );
3817     }
3819     /* Start */
3820     {
3821         eact = create_adjustment_action( "ArcStartAction",
3822                                          _("Start"), _("Start:"),
3823                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
3824                                          "tools.shapes.arc", "start", 0.0,
3825                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3826                                          -360.0, 360.0, 1.0, 10.0,
3827                                          0, 0, 0,
3828                                          sp_arctb_start_value_changed);
3829         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3830     }
3832     /* End */
3833     {
3834         eact = create_adjustment_action( "ArcEndAction",
3835                                          _("End"), _("End:"),
3836                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
3837                                          "tools.shapes.arc", "end", 0.0,
3838                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3839                                          -360.0, 360.0, 1.0, 10.0,
3840                                          0, 0, 0,
3841                                          sp_arctb_end_value_changed);
3842         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3843     }
3845     /* Segments / Pie checkbox */
3846     {
3847         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3849         GtkTreeIter iter;
3850         gtk_list_store_append( model, &iter );
3851         gtk_list_store_set( model, &iter,
3852                             0, _("Closed arc"),
3853                             1, _("Switch to segment (closed shape with two radii)"),
3854                             2, "circle_closed_arc",
3855                             -1 );
3857         gtk_list_store_append( model, &iter );
3858         gtk_list_store_set( model, &iter,
3859                             0, _("Open Arc"),
3860                             1, _("Switch to arc (unclosed shape)"),
3861                             2, "circle_open_arc",
3862                             -1 );
3864         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3865         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3866         g_object_set_data( holder, "open_action", act );
3868         ege_select_one_action_set_appearance( act, "full" );
3869         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3870         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3871         ege_select_one_action_set_icon_column( act, 2 );
3872         ege_select_one_action_set_tooltip_column( act, 1  );
3874         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3875         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3876         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3877         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3878     }
3880     /* Make Whole */
3881     {
3882         InkAction* inky = ink_action_new( "ArcResetAction",
3883                                           _("Make whole"),
3884                                           _("Make the shape a whole ellipse, not arc or segment"),
3885                                           "reset_circle",
3886                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3887         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3888         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3889         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3890         g_object_set_data( holder, "make_whole", inky );
3891     }
3893     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3894     // sensitivize make whole and open checkbox
3895     {
3896         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3897         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3898         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3899     }
3902     sigc::connection *connection = new sigc::connection(
3903         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3904         );
3905     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3906     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3912 // toggle button callbacks and updaters
3914 //########################
3915 //##      Dropper       ##
3916 //########################
3918 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3919     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3920     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3921     if ( set_action ) {
3922         if ( gtk_toggle_action_get_active( act ) ) {
3923             gtk_action_set_sensitive( set_action, TRUE );
3924         } else {
3925             gtk_action_set_sensitive( set_action, FALSE );
3926         }
3927     }
3929     spinbutton_defocus(GTK_OBJECT(tbl));
3932 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3933     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3934     spinbutton_defocus(GTK_OBJECT(tbl));
3938 /**
3939  * Dropper auxiliary toolbar construction and setup.
3940  *
3941  * TODO: Would like to add swatch of current color.
3942  * TODO: Add queue of last 5 or so colors selected with new swatches so that
3943  *       can drag and drop places. Will provide a nice mixing palette.
3944  */
3945 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3947     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3949     {
3950         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
3951         ege_output_action_set_use_markup( act, TRUE );
3952         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3953     }
3955     {
3956         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3957                                                       _("Pick opacity"),
3958                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3959                                                       NULL,
3960                                                       Inkscape::ICON_SIZE_DECORATION );
3961         g_object_set( act, "short_label", _("Pick"), NULL );
3962         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3963         g_object_set_data( holder, "pick_action", act );
3964         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3965         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3966     }
3968     {
3969         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3970                                                       _("Assign opacity"),
3971                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3972                                                       NULL,
3973                                                       Inkscape::ICON_SIZE_DECORATION );
3974         g_object_set( act, "short_label", _("Assign"), NULL );
3975         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3976         g_object_set_data( holder, "set_action", act );
3977         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3978         // make sure it's disabled if we're not picking alpha
3979         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3980         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3981     }
3985 //########################
3986 //##    Text Toolbox    ##
3987 //########################
3988 /*
3989 static void
3990 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3992     //Call back for letter sizing spinbutton
3995 static void
3996 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3998     //Call back for line height spinbutton
4001 static void
4002 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4004     //Call back for horizontal kerning spinbutton
4007 static void
4008 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4010     //Call back for vertical kerning spinbutton
4013 static void
4014 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4016     //Call back for letter rotation spinbutton
4017 }*/
4019 namespace {
4021 bool popdown_visible = false;
4022 bool popdown_hasfocus = false;
4024 void
4025 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4027     SPStyle *query =
4028         sp_style_new (SP_ACTIVE_DOCUMENT);
4030     int result_fontspec =
4031         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4033     int result_family =
4034         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4036     int result_style =
4037         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4039     int result_numbers =
4040         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4042     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4044     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4045     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4046     {
4047         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4049         if (repr)
4050         {
4051             sp_style_read_from_repr (query, repr);
4052         }
4053         else
4054         {
4055             return;
4056         }
4057     }
4059     if (query->text)
4060     {
4061         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4062             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4063             gtk_entry_set_text (GTK_ENTRY (entry), "");
4065         } else if (query->text->font_specification.value || query->text->font_family.value) {
4067             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4069             // Get the font that corresponds
4070             Glib::ustring familyName;
4072             font_instance * font = font_factory::Default()->FaceFromStyle(query);
4073             if (font) {
4074                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4075                 font->Unref();
4076                 font = NULL;
4077             }
4079             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4081             Gtk::TreePath path;
4082             try {
4083                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4084             } catch (...) {
4085                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4086                 return;
4087             }
4089             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4090             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4092             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4094             gtk_tree_selection_select_path (tselection, path.gobj());
4095             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4097             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4098         }
4100         //Size
4101         GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4102         char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4103         g_object_set_data (tbl, "size-block", gpointer(1));
4104         gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4105         g_object_set_data (tbl, "size-block", gpointer(0));
4106         free (str);
4108         //Anchor
4109         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4110         {
4111             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4112             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4113             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4114             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4115         }
4116         else
4117         {
4118             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4119             {
4120                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4121                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4122                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4123                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4124             }
4125             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4126             {
4127                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4128                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4129                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4130                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4131             }
4132             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4133             {
4134                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4135                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4136                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4137                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4138             }
4139         }
4141         //Style
4142         {
4143             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4145             gboolean active = gtk_toggle_button_get_active (button);
4146             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4148             if (active != check)
4149             {
4150                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4151                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4152                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4153             }
4154         }
4156         {
4157             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4159             gboolean active = gtk_toggle_button_get_active (button);
4160             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4162             if (active != check)
4163             {
4164                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4165                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4166                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4167             }
4168         }
4170         //Orientation
4171         //locking both buttons, changing one affect all group (both)
4172         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4173         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4175         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4176         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4178         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4179         {
4180             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4181         }
4182         else
4183         {
4184             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4185         }
4186         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4187         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4188     }
4190     sp_style_unref(query);
4193 void
4194 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4196     sp_text_toolbox_selection_changed (selection, tbl);
4199 void
4200 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4202     sp_text_toolbox_selection_changed (NULL, tbl);
4205 void
4206 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
4207                                 GObject             *tbl)
4209     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
4210     GtkTreeModel *model = 0;
4211     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4212     GtkTreeIter   iter;
4213     char         *family = 0;
4215     gdk_pointer_ungrab (GDK_CURRENT_TIME);
4216     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4218     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4219         return;
4220     }
4222     gtk_tree_model_get (model, &iter, 0, &family, -1);
4224     if (g_object_get_data (G_OBJECT (selection), "block"))
4225     {
4226         gtk_entry_set_text (GTK_ENTRY (entry), family);
4227         return;
4228     }
4230     gtk_entry_set_text (GTK_ENTRY (entry), family);
4232     SPStyle *query =
4233         sp_style_new (SP_ACTIVE_DOCUMENT);
4235     int result_fontspec =
4236         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4238     font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4240     SPCSSAttr *css = sp_repr_css_attr_new ();
4243     // First try to get the font spec from the stored value
4244     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4246     if (fontSpec.empty()) {
4247         // Construct a new font specification if it does not yet exist
4248         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4249         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4250         fontFromStyle->Unref();
4251     }
4253     if (!fontSpec.empty()) {
4254         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4255         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4256             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4257             if (font) {
4258                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4260                 // Set all the these just in case they were altered when finding the best
4261                 // match for the new family and old style...
4263                 gchar c[256];
4265                 font->Family(c, 256);
4266                 sp_repr_css_set_property (css, "font-family", c);
4268                 font->Attribute( "weight", c, 256);
4269                 sp_repr_css_set_property (css, "font-weight", c);
4271                 font->Attribute("style", c, 256);
4272                 sp_repr_css_set_property (css, "font-style", c);
4274                 font->Attribute("stretch", c, 256);
4275                 sp_repr_css_set_property (css, "font-stretch", c);
4277                 font->Attribute("variant", c, 256);
4278                 sp_repr_css_set_property (css, "font-variant", c);
4280                 font->Unref();
4281             }
4282         }
4283     }
4285     // If querying returned nothing, set the default style of the tool (for new texts)
4286     if (result_fontspec == QUERY_STYLE_NOTHING)
4287     {
4288         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4289         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4290     }
4291     else
4292     {
4293         sp_desktop_set_style (desktop, css, true, true);
4294     }
4296     sp_style_unref(query);
4298     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4299                                    _("Text: Change font family"));
4300     sp_repr_css_attr_unref (css);
4301     free (family);
4302     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4304     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4307 void
4308 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
4309                                        GObject      *tbl)
4311     const char *family = gtk_entry_get_text (entry);
4313     try {
4314         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4315         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4316         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4317         gtk_tree_selection_select_path (selection, path.gobj());
4318         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4319         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4320     } catch (...) {
4321         if (family && strlen (family))
4322         {
4323             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4324         }
4325     }
4328 void
4329 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
4330                                    gpointer          data)
4332     if (g_object_get_data (G_OBJECT (button), "block")) return;
4333     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4334     int prop = GPOINTER_TO_INT(data);
4336     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4337     SPCSSAttr *css = sp_repr_css_attr_new ();
4339     switch (prop)
4340     {
4341         case 0:
4342         {
4343             sp_repr_css_set_property (css, "text-anchor", "start");
4344             sp_repr_css_set_property (css, "text-align", "start");
4345             break;
4346         }
4347         case 1:
4348         {
4349             sp_repr_css_set_property (css, "text-anchor", "middle");
4350             sp_repr_css_set_property (css, "text-align", "center");
4351             break;
4352         }
4354         case 2:
4355         {
4356             sp_repr_css_set_property (css, "text-anchor", "end");
4357             sp_repr_css_set_property (css, "text-align", "end");
4358             break;
4359         }
4361         case 3:
4362         {
4363             sp_repr_css_set_property (css, "text-anchor", "start");
4364             sp_repr_css_set_property (css, "text-align", "justify");
4365             break;
4366         }
4367     }
4369     SPStyle *query =
4370         sp_style_new (SP_ACTIVE_DOCUMENT);
4371     int result_numbers =
4372         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4374     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4375     if (result_numbers == QUERY_STYLE_NOTHING)
4376     {
4377         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4378     }
4380     sp_style_unref(query);
4382     sp_desktop_set_style (desktop, css, true, true);
4383     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4384                                    _("Text: Change alignment"));
4385     sp_repr_css_attr_unref (css);
4387     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4390 void
4391 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
4392                                gpointer          data)
4394     if (g_object_get_data (G_OBJECT (button), "block")) return;
4396     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4397     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4398     int          prop       = GPOINTER_TO_INT(data);
4399     bool         active     = gtk_toggle_button_get_active (button);
4401     SPStyle *query =
4402         sp_style_new (SP_ACTIVE_DOCUMENT);
4404     int result_fontspec =
4405         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4407     int result_family =
4408         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4410     int result_style =
4411         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4413     int result_numbers =
4414         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4416     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
4417     Glib::ustring newFontSpec = "";
4419     if (fontSpec.empty()) {
4420         // Construct a new font specification if it does not yet exist
4421         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4422         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4423         fontFromStyle->Unref();
4424     }
4426     switch (prop)
4427     {
4428         case 0:
4429         {
4430             if (!fontSpec.empty()) {
4431                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4432             }
4433             if (fontSpec != newFontSpec) {
4434                 // Don't even set the bold if the font didn't exist on the system
4435                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4436             }
4437             break;
4438         }
4440         case 1:
4441         {
4442             if (!fontSpec.empty()) {
4443                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4444             }
4445             if (fontSpec != newFontSpec) {
4446                 // Don't even set the italic if the font didn't exist on the system
4447                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4448             }
4449             break;
4450         }
4451     }
4453     if (!newFontSpec.empty()) {
4454         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4455     }
4457     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4458     if (result_fontspec == QUERY_STYLE_NOTHING)
4459     {
4460         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4461     }
4463     sp_style_unref(query);
4465     sp_desktop_set_style (desktop, css, true, true);
4466     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4467                                    _("Text: Change font style"));
4468     sp_repr_css_attr_unref (css);
4470     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4473 void
4474 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
4475                                      gpointer         data)
4477     if (g_object_get_data (G_OBJECT (button), "block")) {
4478         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4479         return;
4480     }
4482     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
4483     SPCSSAttr   *css        = sp_repr_css_attr_new ();
4484     int          prop       = GPOINTER_TO_INT(data);
4486     switch (prop)
4487     {
4488         case 0:
4489         {
4490             sp_repr_css_set_property (css, "writing-mode", "lr");
4491             break;
4492         }
4494         case 1:
4495         {
4496             sp_repr_css_set_property (css, "writing-mode", "tb");
4497             break;
4498         }
4499     }
4501     SPStyle *query =
4502         sp_style_new (SP_ACTIVE_DOCUMENT);
4503     int result_numbers =
4504         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4506     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4507     if (result_numbers == QUERY_STYLE_NOTHING)
4508     {
4509         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4510     }
4512     sp_desktop_set_style (desktop, css, true, true);
4513     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4514                                    _("Text: Change orientation"));
4515     sp_repr_css_attr_unref (css);
4517     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4520 gboolean
4521 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4523     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4524     if (!desktop) return FALSE;
4526     switch (get_group0_keyval (event)) {
4527         case GDK_Escape: // defocus
4528             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4529             sp_text_toolbox_selection_changed (NULL, tbl); // update
4530             return TRUE; // I consumed the event
4531             break;
4532     }
4533     return FALSE;
4536 gboolean
4537 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4539     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4540     if (!desktop) return FALSE;
4542     switch (get_group0_keyval (event)) {
4543         case GDK_KP_Enter:
4544         case GDK_Return:
4545         case GDK_Escape: // defocus
4546             gtk_widget_hide (w);
4547             popdown_visible = false;
4548             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4549             return TRUE; // I consumed the event
4550             break;
4551         case GDK_w:
4552         case GDK_W:
4553             if (event->state & GDK_CONTROL_MASK) {
4554                 gtk_widget_hide (w);
4555                 popdown_visible = false;
4556                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4557                 return TRUE; // I consumed the event
4558             }
4559             break;
4560     }
4561     return FALSE;
4565 void
4566 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
4567                                GObject     *tbl)
4569     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4571     if (g_object_get_data (tbl, "size-block")) return;
4573     // If this is not from selecting a size in the list (in which case get_active will give the
4574     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4575     // process this event. This fixes GTK's stupid insistence on sending an activate change every
4576     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4577     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4578         return;
4580     gchar *endptr;
4581     gdouble value = -1;
4582     char *text = gtk_combo_box_get_active_text (cbox);
4583     if (text) {
4584         value = g_strtod (text, &endptr);
4585         if (endptr == text) // conversion failed, non-numeric input
4586             value = -1;
4587         free (text);
4588     }
4589     if (value <= 0) {
4590         return; // could not parse value
4591     }
4593     SPCSSAttr *css = sp_repr_css_attr_new ();
4594     Inkscape::CSSOStringStream osfs;
4595     osfs << value;
4596     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4598     SPStyle *query =
4599         sp_style_new (SP_ACTIVE_DOCUMENT);
4600     int result_numbers =
4601         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4603     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4604     if (result_numbers == QUERY_STYLE_NOTHING)
4605     {
4606         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4607     }
4609     sp_style_unref(query);
4611     sp_desktop_set_style (desktop, css, true, true);
4612     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4613                                    _("Text: Change font size"));
4614     sp_repr_css_attr_unref (css);
4616     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4619 gboolean
4620 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
4622     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4623     if (!desktop) return FALSE;
4625     if (!g_object_get_data (tbl, "esc-pressed")) {
4626         g_object_set_data (tbl, "enter-pressed", gpointer(1));
4627         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4628         sp_text_toolbox_size_changed (cbox, tbl);
4629         g_object_set_data (tbl, "enter-pressed", gpointer(0));
4630     }
4631     return FALSE; // I consumed the event
4635 gboolean
4636 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4638     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4639     if (!desktop) return FALSE;
4641     switch (get_group0_keyval (event)) {
4642         case GDK_Escape: // defocus
4643             g_object_set_data (tbl, "esc-pressed", gpointer(1));
4644             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4645             g_object_set_data (tbl, "esc-pressed", gpointer(0));
4646             return TRUE; // I consumed the event
4647             break;
4648         case GDK_Return: // defocus
4649         case GDK_KP_Enter:
4650             g_object_set_data (tbl, "enter-pressed", gpointer(1));
4651             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4652             sp_text_toolbox_size_changed (cbox, tbl);
4653             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4654             g_object_set_data (tbl, "enter-pressed", gpointer(0));
4655             return TRUE; // I consumed the event
4656             break;
4657     }
4658     return FALSE;
4661 void
4662 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
4663                                          GObject            *tbl)
4665     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4666     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4667     int x, y;
4669     if (!popdown_visible)
4670     {
4671         gdk_window_get_origin (widget->window, &x, &y);
4672         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4673         gtk_widget_show_all (popdown);
4674         //sp_transientize (popdown);
4676         gdk_pointer_grab (widget->window, TRUE,
4677                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4678                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4679                                         GDK_POINTER_MOTION_MASK),
4680                           NULL, NULL, GDK_CURRENT_TIME);
4682         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4684         popdown_visible = true;
4685     }
4686     else
4687     {
4688         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4689         gdk_pointer_ungrab (GDK_CURRENT_TIME);
4690         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4691         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4692         gtk_widget_hide (popdown);
4693         popdown_visible = false;
4694     }
4697 gboolean
4698 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
4699                                  GdkEventFocus    */*event*/,
4700                                  GObject          */*tbl*/)
4702     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4703     return FALSE;
4706 gboolean
4707 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
4708                                    GdkEventFocus    */*event*/,
4709                                    GObject          */*tbl*/)
4711     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4713     if (popdown_hasfocus) {
4714         gtk_widget_hide (popdown);
4715         popdown_hasfocus = false;
4716         popdown_visible = false;
4717         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4718         return TRUE;
4719     }
4720     return FALSE;
4723 gboolean
4724 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
4725                                    GdkEventFocus    */*event*/,
4726                                    GObject          */*tbl*/)
4728     popdown_hasfocus = true;
4729     return TRUE;
4733 void
4734 cell_data_func  (GtkTreeViewColumn */*column*/,
4735                  GtkCellRenderer   *cell,
4736                  GtkTreeModel      *tree_model,
4737                  GtkTreeIter       *iter,
4738                  gpointer           /*data*/)
4740     char        *family,
4741         *family_escaped,
4742         *sample_escaped;
4744     static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4746     gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4748     family_escaped = g_markup_escape_text (family, -1);
4749     sample_escaped = g_markup_escape_text (sample, -1);
4751     std::stringstream markup;
4752     markup << family_escaped << "  <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4753     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4755     free (family);
4756     free (family_escaped);
4757     free (sample_escaped);
4760 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4761     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4762     if (completion) {
4763         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4764         g_object_unref (completion);
4765     }
4768 GtkWidget*
4769 sp_text_toolbox_new (SPDesktop *desktop)
4771     GtkWidget   *tbl = gtk_hbox_new (FALSE, 0);
4773     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4774     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4776     GtkTooltips *tt = gtk_tooltips_new();
4777     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4779     ////////////Family
4780     //Window
4781     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4782     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4784     //Entry
4785     GtkWidget           *entry = gtk_entry_new ();
4786     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4787     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
4788     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4789     gtk_entry_completion_set_text_column (completion, 0);
4790     gtk_entry_completion_set_minimum_key_length (completion, 1);
4791     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4792     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4793     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4794     aux_toolbox_space (tbl, 1);
4795     gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4796     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4798     //Button
4799     GtkWidget   *button = gtk_button_new ();
4800     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4801     gtk_box_pack_start      (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4803     //Popdown
4804     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
4805     GtkWidget           *treeview = gtk_tree_view_new ();
4807     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
4808     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
4809     gtk_tree_view_column_pack_start (column, cell, FALSE);
4810     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4811     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4812     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4814     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4815     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4816     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4818     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4820     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4821     gtk_container_add (GTK_CONTAINER (sw), treeview);
4823     gtk_container_add (GTK_CONTAINER (window), sw);
4824     gtk_widget_set_size_request (window, 300, 450);
4826     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4827     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4828     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4830     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4832     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4833     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
4834     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4836     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4837     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4839     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4840     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4841     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4842     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4843     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4845     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4846     aux_toolbox_space (tbl, 1);
4847     GtkWidget *box = gtk_event_box_new ();
4848     gtk_container_add (GTK_CONTAINER (box), image);
4849     gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4850     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4851     GtkTooltips *tooltips = gtk_tooltips_new ();
4852     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4853     gtk_widget_hide (GTK_WIDGET (box));
4854     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4856     ////////////Size
4857     const char *sizes[] = {
4858         "4", "6", "8", "9", "10", "11", "12", "13", "14",
4859         "16", "18", "20", "22", "24", "28",
4860         "32", "36", "40", "48", "56", "64", "72", "144"
4861     };
4863     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4864     for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4865     gtk_widget_set_size_request (cbox, 80, -1);
4866     aux_toolbox_space (tbl, 1);
4867     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4868     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4869     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4870     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
4871     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
4873     //spacer
4874     aux_toolbox_space (tbl, 4);
4875     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4877     ////////////Text anchor
4878     GtkWidget *group   = gtk_radio_button_new (NULL);
4879     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
4880     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4882     // left
4883     GtkWidget *rbutton = group;
4884     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4885     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4886     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4888     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4889     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
4890     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4891     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4893     // center
4894     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4895     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4896     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4897     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4899     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4900     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
4901     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4902     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4904     // right
4905     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4906     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4907     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4908     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4910     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4911     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
4912     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4913     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4915     // fill
4916     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4917     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4918     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4919     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4921     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4922     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
4923     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4924     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4926     aux_toolbox_space (tbl, 1);
4927     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4929     //spacer
4930     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4932     ////////////Text style
4933     row = gtk_hbox_new (FALSE, 4);
4935     // bold
4936     rbutton = gtk_toggle_button_new ();
4937     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4938     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4939     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4940     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4942     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4943     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
4944     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4946     // italic
4947     rbutton = gtk_toggle_button_new ();
4948     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4949     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4950     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4951     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4953     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4954     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
4955     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4957     aux_toolbox_space (tbl, 1);
4958     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4960     //spacer
4961     gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4963     ////////////Text orientation
4964     group   = gtk_radio_button_new (NULL);
4965     row     = gtk_hbox_new (FALSE, 4);
4966     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4968     // horizontal
4969     rbutton = group;
4970     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4971     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4972     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4973     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4975     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4976     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4977     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4979     // vertical
4980     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4981     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4982     gtk_container_add           (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4983     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4984     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4986     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
4987     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
4988     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4989     gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4992     //watch selection
4993     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4995     sigc::connection *c_selection_changed =
4996         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4997                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4998     pool->add_connection ("selection-changed", c_selection_changed);
5000     sigc::connection *c_selection_modified =
5001         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5002                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5003     pool->add_connection ("selection-modified", c_selection_modified);
5005     sigc::connection *c_subselection_changed =
5006         new sigc::connection (desktop->connectToolSubselectionChanged
5007                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5008     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5010     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5013     gtk_widget_show_all (tbl);
5014     return tbl;
5016 } // end of sp_text_toolbox_new()
5018 }//<unnamed> namespace
5021 //#########################
5022 //##      Connector      ##
5023 //#########################
5025 static void sp_connector_path_set_avoid(void)
5027     cc_selection_set_avoid(true);
5031 static void sp_connector_path_set_ignore(void)
5033     cc_selection_set_avoid(false);
5038 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5040     // quit if run by the _changed callbacks
5041     if (g_object_get_data( tbl, "freeze" )) {
5042         return;
5043     }
5045     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5046     SPDocument *doc = sp_desktop_document(desktop);
5048     if (!sp_document_get_undo_sensitive(doc))
5049     {
5050         return;
5051     }
5053     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5055     if ( repr->attribute("inkscape:connector-spacing") ) {
5056         gdouble priorValue = gtk_adjustment_get_value(adj);
5057         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5058         if ( priorValue == gtk_adjustment_get_value(adj) ) {
5059             return;
5060         }
5061     } else if ( adj->value == defaultConnSpacing ) {
5062         return;
5063     }
5065     // in turn, prevent callbacks from responding
5066     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5068     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5069     SP_OBJECT(desktop->namedview)->updateRepr();
5071     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5072     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5073         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5074         NR::Matrix m = NR::identity();
5075         avoid_item_move(&m, item);
5076     }
5078     if (items) {
5079         g_slist_free(items);
5080     }
5082     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5083             _("Change connector spacing"));
5085     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5087     spinbutton_defocus(GTK_OBJECT(tbl));
5090 static void sp_connector_graph_layout(void)
5092     if (!SP_ACTIVE_DESKTOP) return;
5094     // hack for clones, see comment in align-and-distribute.cpp
5095     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5096     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5098     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5100     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5102     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5105 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5107     if ( gtk_toggle_action_get_active( act ) ) {
5108         prefs_set_string_attribute("tools.connector", "directedlayout",
5109                 "true");
5110     } else {
5111         prefs_set_string_attribute("tools.connector", "directedlayout",
5112                 "false");
5113     }
5116 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5118     if ( gtk_toggle_action_get_active( act ) ) {
5119         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5120                 "true");
5121     } else {
5122         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5123                 "false");
5124     }
5128 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5130     prefs_set_double_attribute("tools.connector", "length", adj->value);
5131     spinbutton_defocus(GTK_OBJECT(tbl));
5134 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5135                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5136                                             bool /*is_interactive*/, gpointer data)
5138     GtkWidget *tbl = GTK_WIDGET(data);
5140     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5141         return;
5142     }
5143     if (strcmp(name, "inkscape:connector-spacing") != 0) {
5144         return;
5145     }
5147     GtkAdjustment *adj = (GtkAdjustment*)
5148             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5149     gdouble spacing = defaultConnSpacing;
5150     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5152     gtk_adjustment_set_value(adj, spacing);
5156 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5157     NULL, /* child_added */
5158     NULL, /* child_removed */
5159     connector_tb_event_attr_changed,
5160     NULL, /* content_changed */
5161     NULL  /* order_changed */
5162 };
5165 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5167     {
5168         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5169                                           _("Avoid"),
5170                                           _("Make connectors avoid selected objects"),
5171                                           "connector_avoid",
5172                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5173         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5174         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5175     }
5177     {
5178         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5179                                           _("Ignore"),
5180                                           _("Make connectors ignore selected objects"),
5181                                           "connector_ignore",
5182                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5183         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5184         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5185     }
5187     EgeAdjustmentAction* eact = 0;
5189     // Spacing spinbox
5190     eact = create_adjustment_action( "ConnectorSpacingAction",
5191                                      _("Connector Spacing"), _("Spacing:"),
5192                                      _("The amount of space left around objects by auto-routing connectors"),
5193                                      "tools.connector", "spacing", defaultConnSpacing,
5194                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5195                                      0, 100, 1.0, 10.0,
5196                                      0, 0, 0,
5197                                      connector_spacing_changed, 1, 0 );
5198     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5200     // Graph (connector network) layout
5201     {
5202         InkAction* inky = ink_action_new( "ConnectorGraphAction",
5203                                           _("Graph"),
5204                                           _("Nicely arrange selected connector network"),
5205                                           "graph_layout",
5206                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5207         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5208         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5209     }
5211     // Default connector length spinbox
5212     eact = create_adjustment_action( "ConnectorLengthAction",
5213                                      _("Connector Length"), _("Length:"),
5214                                      _("Ideal length for connectors when layout is applied"),
5215                                      "tools.connector", "length", 100,
5216                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5217                                      10, 1000, 10.0, 100.0,
5218                                      0, 0, 0,
5219                                      connector_length_changed, 1, 0 );
5220     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5223     // Directed edges toggle button
5224     {
5225         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5226                                                       _("Downwards"),
5227                                                       _("Make connectors with end-markers (arrows) point downwards"),
5228                                                       "directed_graph",
5229                                                       Inkscape::ICON_SIZE_DECORATION );
5230         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5232         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5233         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5234                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5236         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5237     }
5239     // Avoid overlaps toggle button
5240     {
5241         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5242                                                       _("Remove overlaps"),
5243                                                       _("Do not allow overlapping shapes"),
5244                                                       "remove_overlaps",
5245                                                       Inkscape::ICON_SIZE_DECORATION );
5246         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5248         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5249         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5250                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5252         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5253     }
5255     // Code to watch for changes to the connector-spacing attribute in
5256     // the XML.
5257     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5258     g_assert(repr != NULL);
5260     purge_repr_listener( holder, holder );
5262     if (repr) {
5263         g_object_set_data( holder, "repr", repr );
5264         Inkscape::GC::anchor(repr);
5265         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5266         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5267     }
5268 } // end of sp_connector_toolbox_prep()
5271 //#########################
5272 //##     Paintbucket     ##
5273 //#########################
5275 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5277     gint channels = ege_select_one_action_get_active( act );
5278     flood_channels_set_channels( channels );
5281 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5283     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5286 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5288     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5291 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5293     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5294     SPUnit const *unit = tracker->getActiveUnit();
5296     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5298     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5301 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5303     // FIXME: make defaults settable via Inkscape Options
5304     struct KeyValue {
5305         char const *key;
5306         double value;
5307     } const key_values[] = {
5308         {"threshold", 15},
5309         {"offset", 0.0}
5310     };
5312     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5313         KeyValue const &kv = key_values[i];
5314         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5315         if ( adj ) {
5316             gtk_adjustment_set_value(adj, kv.value);
5317         }
5318     }
5320     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5321     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5322     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5323     ege_select_one_action_set_active( autogap_action, 0 );
5326 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5328     EgeAdjustmentAction* eact = 0;
5330     {
5331         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5333         GList* items = 0;
5334         gint count = 0;
5335         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5336         {
5337             GtkTreeIter iter;
5338             gtk_list_store_append( model, &iter );
5339             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5340             count++;
5341         }
5342         g_list_free( items );
5343         items = 0;
5344         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5345         g_object_set( act1, "short_label", _("Fill by:"), NULL );
5346         ege_select_one_action_set_appearance( act1, "compact" );
5347         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5348         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5349         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5350         g_object_set_data( holder, "channels_action", act1 );
5351     }
5353     // Spacing spinbox
5354     {
5355         eact = create_adjustment_action(
5356             "ThresholdAction",
5357             _("Fill Threshold"), _("Threshold:"),
5358             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5359             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5360             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5361             0, 0, 0,
5362             paintbucket_threshold_changed, 1, 0 );
5364         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5365     }
5367     // Create the units menu.
5368     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5369     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5370     if (stored_unit)
5371         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5372     g_object_set_data( holder, "tracker", tracker );
5373     {
5374         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5375         gtk_action_group_add_action( mainActions, act );
5376     }
5378     // Offset spinbox
5379     {
5380         eact = create_adjustment_action(
5381             "OffsetAction",
5382             _("Grow/shrink by"), _("Grow/shrink by:"),
5383             _("The amount to grow (positive) or shrink (negative) the created fill path"),
5384             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5385             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5386             0, 0, 0,
5387             paintbucket_offset_changed, 1, 2);
5388         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5390         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5391     }
5393     /* Auto Gap */
5394     {
5395         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5397         GList* items = 0;
5398         gint count = 0;
5399         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5400         {
5401             GtkTreeIter iter;
5402             gtk_list_store_append( model, &iter );
5403             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5404             count++;
5405         }
5406         g_list_free( items );
5407         items = 0;
5408         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5409         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5410         ege_select_one_action_set_appearance( act2, "compact" );
5411         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5412         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5413         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5414         g_object_set_data( holder, "autogap_action", act2 );
5415     }
5417     /* Reset */
5418     {
5419         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5420                                           _("Defaults"),
5421                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5422                                           GTK_STOCK_CLEAR );
5423         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5424         gtk_action_group_add_action( mainActions, act );
5425         gtk_action_set_sensitive( act, TRUE );
5426     }
5430 /*
5431   Local Variables:
5432   mode:c++
5433   c-file-style:"stroustrup"
5434   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5435   indent-tabs-mode:nil
5436   fill-column:99
5437   End:
5438 */
5439 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :