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"
57 #include "live_effects/effect.h"
59 #include "inkscape.h"
60 #include "conn-avoid-ref.h"
63 #include "select-toolbar.h"
64 #include "gradient-toolbar.h"
66 #include "connector-context.h"
67 #include "node-context.h"
68 #include "draw-context.h"
69 #include "shape-editor.h"
70 #include "tweak-context.h"
71 #include "sp-rect.h"
72 #include "box3d.h"
73 #include "box3d-context.h"
74 #include "sp-star.h"
75 #include "sp-spiral.h"
76 #include "sp-ellipse.h"
77 #include "sp-text.h"
78 #include "sp-flowtext.h"
79 #include "sp-clippath.h"
80 #include "sp-mask.h"
81 #include "style.h"
82 #include "selection.h"
83 #include "selection-chemistry.h"
84 #include "document-private.h"
85 #include "desktop-style.h"
86 #include "../libnrtype/font-lister.h"
87 #include "../libnrtype/font-instance.h"
88 #include "../connection-pool.h"
89 #include "../prefs-utils.h"
90 #include "../inkscape-stock.h"
91 #include "icon.h"
92 #include "graphlayout/graphlayout.h"
93 #include "interface.h"
94 #include "shortcuts.h"
96 #include "mod360.h"
98 #include "toolbox.h"
100 #include "flood-context.h"
102 #include "ink-action.h"
103 #include "ege-adjustment-action.h"
104 #include "ege-output-action.h"
105 #include "ege-select-one-action.h"
106 #include "helper/unit-tracker.h"
108 #include "svg/css-ostringstream.h"
110 #include "widgets/calligraphic-profile-rename.h"
112 using Inkscape::UnitTracker;
114 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
115 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
117 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
130 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
137 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
138 static Inkscape::IconSize sizeChoices[] = {
139 Inkscape::ICON_SIZE_LARGE_TOOLBAR,
140 Inkscape::ICON_SIZE_SMALL_TOOLBAR,
141 Inkscape::ICON_SIZE_MENU
142 };
143 int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
144 return sizeChoices[index];
145 }
147 static struct {
148 gchar const *type_name;
149 gchar const *data_name;
150 sp_verb_t verb;
151 sp_verb_t doubleclick_verb;
152 } const tools[] = {
153 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
154 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
155 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
156 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
157 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
158 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
159 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
160 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
161 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
162 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
163 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
164 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
165 { "SPEraserContext", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
166 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
167 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
168 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
169 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
170 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
171 { NULL, NULL, 0, 0 }
172 };
174 static struct {
175 gchar const *type_name;
176 gchar const *data_name;
177 GtkWidget *(*create_func)(SPDesktop *desktop);
178 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
179 gchar const *ui_name;
180 gint swatch_verb_id;
181 gchar const *swatch_tool;
182 gchar const *swatch_tip;
183 } const aux_toolboxes[] = {
184 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
185 SP_VERB_INVALID, 0, 0},
186 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
187 SP_VERB_INVALID, 0, 0},
188 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
189 SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
190 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
191 SP_VERB_INVALID, 0, 0},
192 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
193 SP_VERB_CONTEXT_STAR_PREFS, "tools.shapes.star", N_("Style of new stars")},
194 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
195 SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", N_("Style of new rectangles")},
196 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
197 SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", N_("Style of new 3D boxes")},
198 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
199 SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", N_("Style of new ellipses")},
200 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
201 SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral", N_("Style of new spirals")},
202 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
203 SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
204 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
205 SP_VERB_CONTEXT_PEN_PREFS, "tools.freehand.pen", N_("Style of new paths created by Pen")},
206 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
207 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
208 { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
209 SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
210 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
211 SP_VERB_INVALID, 0, 0},
212 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
213 SP_VERB_INVALID, 0, 0},
214 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
215 SP_VERB_INVALID, 0, 0},
216 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
217 SP_VERB_INVALID, 0, 0},
218 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
219 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
220 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
221 };
224 static gchar const * ui_descr =
225 "<ui>"
226 " <toolbar name='SelectToolbar'>"
227 " <toolitem action='EditSelectAll' />"
228 " <toolitem action='EditSelectAllInAllLayers' />"
229 " <toolitem action='EditDeselect' />"
230 " <separator />"
231 " <toolitem action='ObjectRotate90CCW' />"
232 " <toolitem action='ObjectRotate90' />"
233 " <toolitem action='ObjectFlipHorizontally' />"
234 " <toolitem action='ObjectFlipVertically' />"
235 " <separator />"
236 " <toolitem action='SelectionToBack' />"
237 " <toolitem action='SelectionLower' />"
238 " <toolitem action='SelectionRaise' />"
239 " <toolitem action='SelectionToFront' />"
240 " <separator />"
241 " <toolitem action='XAction' />"
242 " <toolitem action='YAction' />"
243 " <toolitem action='WidthAction' />"
244 " <toolitem action='LockAction' />"
245 " <toolitem action='HeightAction' />"
246 " <toolitem action='UnitsAction' />"
247 " <separator />"
248 " <toolitem action='transform_affect_label' />"
249 " <toolitem action='transform_stroke' />"
250 " <toolitem action='transform_corners' />"
251 " <toolitem action='transform_gradient' />"
252 " <toolitem action='transform_pattern' />"
253 " </toolbar>"
255 " <toolbar name='NodeToolbar'>"
256 " <toolitem action='NodeInsertAction' />"
257 " <toolitem action='NodeDeleteAction' />"
258 " <separator />"
259 " <toolitem action='NodeJoinAction' />"
260 " <toolitem action='NodeBreakAction' />"
261 " <separator />"
262 " <toolitem action='NodeJoinSegmentAction' />"
263 " <toolitem action='NodeDeleteSegmentAction' />"
264 " <separator />"
265 " <toolitem action='NodeCuspAction' />"
266 " <toolitem action='NodeSmoothAction' />"
267 " <toolitem action='NodeSymmetricAction' />"
268 " <separator />"
269 " <toolitem action='NodeLineAction' />"
270 " <toolitem action='NodeCurveAction' />"
271 " <separator />"
272 " <toolitem action='ObjectToPath' />"
273 " <toolitem action='StrokeToPath' />"
274 " <separator />"
275 " <toolitem action='NodeXAction' />"
276 " <toolitem action='NodeYAction' />"
277 " <toolitem action='NodeUnitsAction' />"
278 " <separator />"
279 " <toolitem action='ObjectEditClipPathAction' />"
280 " <toolitem action='ObjectEditMaskPathAction' />"
281 " <toolitem action='EditNextLPEParameterAction' />"
282 " <separator />"
283 " <toolitem action='NodesShowHandlesAction' />"
284 " <toolitem action='NodesShowHelperpath' />"
285 " </toolbar>"
287 " <toolbar name='TweakToolbar'>"
288 " <toolitem action='TweakWidthAction' />"
289 " <separator />"
290 " <toolitem action='TweakForceAction' />"
291 " <toolitem action='TweakPressureAction' />"
292 " <separator />"
293 " <toolitem action='TweakModeAction' />"
294 " <separator />"
295 " <toolitem action='TweakFidelityAction' />"
296 " <separator />"
297 " <toolitem action='TweakChannelsLabel' />"
298 " <toolitem action='TweakDoH' />"
299 " <toolitem action='TweakDoS' />"
300 " <toolitem action='TweakDoL' />"
301 " <toolitem action='TweakDoO' />"
302 " </toolbar>"
304 " <toolbar name='ZoomToolbar'>"
305 " <toolitem action='ZoomIn' />"
306 " <toolitem action='ZoomOut' />"
307 " <separator />"
308 " <toolitem action='Zoom1:0' />"
309 " <toolitem action='Zoom1:2' />"
310 " <toolitem action='Zoom2:1' />"
311 " <separator />"
312 " <toolitem action='ZoomSelection' />"
313 " <toolitem action='ZoomDrawing' />"
314 " <toolitem action='ZoomPage' />"
315 " <toolitem action='ZoomPageWidth' />"
316 " <separator />"
317 " <toolitem action='ZoomPrev' />"
318 " <toolitem action='ZoomNext' />"
319 " </toolbar>"
321 " <toolbar name='StarToolbar'>"
322 " <separator />"
323 " <toolitem action='StarStateAction' />"
324 " <separator />"
325 " <toolitem action='FlatAction' />"
326 " <separator />"
327 " <toolitem action='MagnitudeAction' />"
328 " <toolitem action='SpokeAction' />"
329 " <toolitem action='RoundednessAction' />"
330 " <toolitem action='RandomizationAction' />"
331 " <separator />"
332 " <toolitem action='StarResetAction' />"
333 " </toolbar>"
335 " <toolbar name='RectToolbar'>"
336 " <toolitem action='RectStateAction' />"
337 " <toolitem action='RectWidthAction' />"
338 " <toolitem action='RectHeightAction' />"
339 " <toolitem action='RadiusXAction' />"
340 " <toolitem action='RadiusYAction' />"
341 " <toolitem action='RectUnitsAction' />"
342 " <separator />"
343 " <toolitem action='RectResetAction' />"
344 " </toolbar>"
346 " <toolbar name='3DBoxToolbar'>"
347 " <toolitem action='3DBoxAngleXAction' />"
348 " <toolitem action='3DBoxVPXStateAction' />"
349 " <separator />"
350 " <toolitem action='3DBoxAngleYAction' />"
351 " <toolitem action='3DBoxVPYStateAction' />"
352 " <separator />"
353 " <toolitem action='3DBoxAngleZAction' />"
354 " <toolitem action='3DBoxVPZStateAction' />"
355 " </toolbar>"
357 " <toolbar name='SpiralToolbar'>"
358 " <toolitem action='SpiralStateAction' />"
359 " <toolitem action='SpiralRevolutionAction' />"
360 " <toolitem action='SpiralExpansionAction' />"
361 " <toolitem action='SpiralT0Action' />"
362 " <separator />"
363 " <toolitem action='SpiralResetAction' />"
364 " </toolbar>"
366 " <toolbar name='PenToolbar'>"
367 " <toolitem action='FreehandModeActionPenTemp' />"
368 " <toolitem action='FreehandModeActionPen' />"
369 " </toolbar>"
371 " <toolbar name='PencilToolbar'>"
372 " <toolitem action='FreehandModeActionPencilTemp' />"
373 " <toolitem action='FreehandModeActionPencil' />"
374 " <separator />"
375 " <toolitem action='PencilToleranceAction' />"
376 " <separator />"
377 " <toolitem action='PencilResetAction' />"
378 " </toolbar>"
380 " <toolbar name='CalligraphyToolbar'>"
381 " <separator />"
382 " <toolitem action='SetProfileAction'/>"
383 " <toolitem action='SaveDeleteProfileAction'/>"
384 " <separator />"
385 " <toolitem action='CalligraphyWidthAction' />"
386 " <toolitem action='PressureAction' />"
387 " <toolitem action='TraceAction' />"
388 " <toolitem action='ThinningAction' />"
389 " <separator />"
390 " <toolitem action='AngleAction' />"
391 " <toolitem action='TiltAction' />"
392 " <toolitem action='FixationAction' />"
393 " <separator />"
394 " <toolitem action='CapRoundingAction' />"
395 " <separator />"
396 " <toolitem action='TremorAction' />"
397 " <toolitem action='WiggleAction' />"
398 " <toolitem action='MassAction' />"
399 " <separator />"
400 " </toolbar>"
402 " <toolbar name='ArcToolbar'>"
403 " <toolitem action='ArcStateAction' />"
404 " <separator />"
405 " <toolitem action='ArcStartAction' />"
406 " <toolitem action='ArcEndAction' />"
407 " <separator />"
408 " <toolitem action='ArcOpenAction' />"
409 " <separator />"
410 " <toolitem action='ArcResetAction' />"
411 " <separator />"
412 " </toolbar>"
414 " <toolbar name='PaintbucketToolbar'>"
415 " <toolitem action='ChannelsAction' />"
416 " <separator />"
417 " <toolitem action='ThresholdAction' />"
418 " <separator />"
419 " <toolitem action='OffsetAction' />"
420 " <toolitem action='PaintbucketUnitsAction' />"
421 " <separator />"
422 " <toolitem action='AutoGapAction' />"
423 " <separator />"
424 " <toolitem action='PaintbucketResetAction' />"
425 " </toolbar>"
427 " <toolbar name='EraserToolbar'>"
428 " <toolitem action='EraserWidthAction' />"
429 " <separator />"
430 " <toolitem action='EraserModeAction' />"
431 " </toolbar>"
433 " <toolbar name='DropperToolbar'>"
434 " <toolitem action='DropperOpacityAction' />"
435 " <toolitem action='DropperPickAlphaAction' />"
436 " <toolitem action='DropperSetAlphaAction' />"
437 " </toolbar>"
439 " <toolbar name='ConnectorToolbar'>"
440 " <toolitem action='ConnectorAvoidAction' />"
441 " <toolitem action='ConnectorIgnoreAction' />"
442 " <toolitem action='ConnectorSpacingAction' />"
443 " <toolitem action='ConnectorGraphAction' />"
444 " <toolitem action='ConnectorLengthAction' />"
445 " <toolitem action='ConnectorDirectedAction' />"
446 " <toolitem action='ConnectorOverlapAction' />"
447 " </toolbar>"
449 "</ui>"
450 ;
452 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
454 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
456 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
457 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
459 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
460 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
462 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
463 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
466 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
467 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
468 Inkscape::UI::View::View *view, GtkTooltips *tt);
470 class VerbAction : public Gtk::Action {
471 public:
472 static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
474 virtual ~VerbAction();
475 virtual void set_active(bool active = true);
477 protected:
478 virtual Gtk::Widget* create_menu_item_vfunc();
479 virtual Gtk::Widget* create_tool_item_vfunc();
481 virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
482 virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
484 virtual void on_activate();
486 private:
487 Inkscape::Verb* verb;
488 Inkscape::Verb* verb2;
489 Inkscape::UI::View::View *view;
490 GtkTooltips *tooltips;
491 bool active;
493 VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
494 };
497 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
498 {
499 Glib::RefPtr<VerbAction> result;
500 SPAction *action = verb->get_action(view);
501 if ( action ) {
502 //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
503 result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
504 }
506 return result;
507 }
509 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
510 Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(GTK_STOCK_ABOUT), Glib::ustring(verb->get_name()), Glib::ustring(verb->get_tip())),
511 verb(verb),
512 verb2(verb2),
513 view(view),
514 tooltips(tooltips),
515 active(false)
516 {
517 }
519 VerbAction::~VerbAction()
520 {
521 }
523 Gtk::Widget* VerbAction::create_menu_item_vfunc()
524 {
525 Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
526 // g_message("create_menu_item_vfunc() = %p for '%s'", widg, verb->get_id());
527 return widg;
528 }
530 Gtk::Widget* VerbAction::create_tool_item_vfunc()
531 {
532 // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
533 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
534 GtkWidget* toolbox = 0;
535 GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
536 SP_BUTTON_TYPE_TOGGLE,
537 verb,
538 verb2,
539 view,
540 tooltips );
541 if ( active ) {
542 sp_button_toggle_set_down( SP_BUTTON(button), active);
543 }
544 gtk_widget_show_all( button );
545 Gtk::Widget* wrapped = Glib::wrap(button);
546 Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
547 holder->add(*wrapped);
549 // g_message("create_tool_item_vfunc() = %p for '%s'", holder, verb->get_id());
550 return holder;
551 }
553 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
554 {
555 // g_message("connect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
556 Gtk::Action::connect_proxy_vfunc(proxy);
557 }
559 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
560 {
561 // g_message("disconnect_proxy_vfunc(%p) for '%s'", proxy, verb->get_id());
562 Gtk::Action::disconnect_proxy_vfunc(proxy);
563 }
565 void VerbAction::set_active(bool active)
566 {
567 this->active = active;
568 Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
569 for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
570 Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
571 if (ti) {
572 // *should* have one child that is the SPButton
573 Gtk::Widget* child = ti->get_child();
574 if ( child && SP_IS_BUTTON(child->gobj()) ) {
575 SPButton* button = SP_BUTTON(child->gobj());
576 sp_button_toggle_set_down( button, active );
577 }
578 }
579 }
580 }
582 void VerbAction::on_activate()
583 {
584 if ( verb ) {
585 SPAction *action = verb->get_action(view);
586 if ( action ) {
587 sp_action_perform(action, 0);
588 }
589 }
590 }
592 /* Global text entry widgets necessary for update */
593 /* GtkWidget *dropper_rgb_entry,
594 *dropper_opacity_entry ; */
595 // should be made a private member once this is converted to class
597 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
598 connection->disconnect();
599 delete connection;
600 }
602 static void purge_repr_listener( GObject* obj, GObject* tbl )
603 {
604 (void)obj;
605 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
606 if (oldrepr) { // remove old listener
607 sp_repr_remove_listener_by_data(oldrepr, tbl);
608 Inkscape::GC::release(oldrepr);
609 oldrepr = 0;
610 g_object_set_data( tbl, "repr", NULL );
611 }
612 }
614 GtkWidget *
615 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
616 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
617 Inkscape::UI::View::View *view, GtkTooltips *tt)
618 {
619 SPAction *action = verb->get_action(view);
620 if (!action) return NULL;
622 SPAction *doubleclick_action;
623 if (doubleclick_verb)
624 doubleclick_action = doubleclick_verb->get_action(view);
625 else
626 doubleclick_action = NULL;
628 /* fixme: Handle sensitive/unsensitive */
629 /* fixme: Implement sp_button_new_from_action */
630 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
631 gtk_widget_show(b);
634 unsigned int shortcut = sp_shortcut_get_primary(verb);
635 if (shortcut) {
636 gchar key[256];
637 sp_ui_shortcut_string(shortcut, key);
638 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
639 if ( t ) {
640 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
641 }
642 g_free(tip);
643 } else {
644 if ( t ) {
645 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
646 }
647 }
649 return b;
650 }
653 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
654 {
655 SPAction* targetAction = SP_ACTION(user_data);
656 if ( targetAction ) {
657 sp_action_perform( targetAction, NULL );
658 }
659 }
661 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
662 {
663 if ( data ) {
664 GtkAction* act = GTK_ACTION(data);
665 gtk_action_set_sensitive( act, sensitive );
666 }
667 }
669 static SPActionEventVector action_event_vector = {
670 {NULL},
671 NULL,
672 NULL,
673 sp_action_action_set_sensitive,
674 NULL,
675 NULL
676 };
678 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
679 {
680 GtkAction* act = 0;
682 SPAction* targetAction = verb->get_action(view);
683 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
684 act = GTK_ACTION(inky);
685 gtk_action_set_sensitive( act, targetAction->sensitive );
687 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
689 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
690 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
692 return act;
693 }
695 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
696 {
697 Inkscape::UI::View::View *view = desktop;
698 gint verbsToUse[] = {
699 // disabled until we have icons for them:
700 //find
701 //SP_VERB_EDIT_TILE,
702 //SP_VERB_EDIT_UNTILE,
703 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
704 SP_VERB_DIALOG_DISPLAY,
705 SP_VERB_DIALOG_FILL_STROKE,
706 SP_VERB_DIALOG_NAMEDVIEW,
707 SP_VERB_DIALOG_TEXT,
708 SP_VERB_DIALOG_XML_EDITOR,
709 SP_VERB_EDIT_CLONE,
710 SP_VERB_EDIT_COPY,
711 SP_VERB_EDIT_CUT,
712 SP_VERB_EDIT_DUPLICATE,
713 SP_VERB_EDIT_PASTE,
714 SP_VERB_EDIT_REDO,
715 SP_VERB_EDIT_UNDO,
716 SP_VERB_EDIT_UNLINK_CLONE,
717 SP_VERB_FILE_EXPORT,
718 SP_VERB_FILE_IMPORT,
719 SP_VERB_FILE_NEW,
720 SP_VERB_FILE_OPEN,
721 SP_VERB_FILE_PRINT,
722 SP_VERB_FILE_SAVE,
723 SP_VERB_OBJECT_TO_CURVE,
724 SP_VERB_SELECTION_GROUP,
725 SP_VERB_SELECTION_OUTLINE,
726 SP_VERB_SELECTION_UNGROUP,
727 SP_VERB_ZOOM_1_1,
728 SP_VERB_ZOOM_1_2,
729 SP_VERB_ZOOM_2_1,
730 SP_VERB_ZOOM_DRAWING,
731 SP_VERB_ZOOM_IN,
732 SP_VERB_ZOOM_NEXT,
733 SP_VERB_ZOOM_OUT,
734 SP_VERB_ZOOM_PAGE,
735 SP_VERB_ZOOM_PAGE_WIDTH,
736 SP_VERB_ZOOM_PREV,
737 SP_VERB_ZOOM_SELECTION,
738 };
740 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
742 static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
743 Glib::RefPtr<Gtk::ActionGroup> mainActions;
744 if ( groups.find(desktop) != groups.end() ) {
745 mainActions = groups[desktop];
746 }
748 if ( !mainActions ) {
749 mainActions = Gtk::ActionGroup::create("main");
750 groups[desktop] = mainActions;
751 }
753 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
754 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
755 if ( verb ) {
756 if (!mainActions->get_action(verb->get_id())) {
757 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
758 mainActions->add(Glib::wrap(act));
759 }
760 }
761 }
763 if ( !mainActions->get_action("ToolZoom") ) {
764 GtkTooltips *tt = gtk_tooltips_new();
765 for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
766 Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
767 if ( va ) {
768 mainActions->add(va);
769 if ( i == 0 ) {
770 va->set_active(true);
771 }
772 }
773 }
774 }
777 return mainActions;
778 }
781 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
782 {
783 gtk_widget_set_size_request( widget,
784 widget->allocation.width,
785 widget->allocation.height );
786 }
788 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
789 {
790 gtk_widget_set_size_request( widget, -1, -1 );
791 }
795 GtkWidget *
796 sp_tool_toolbox_new()
797 {
798 GtkTooltips *tt = gtk_tooltips_new();
799 GtkWidget* tb = gtk_toolbar_new();
800 gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
801 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
803 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
804 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
806 gtk_widget_set_sensitive(tb, FALSE);
808 GtkWidget *hb = gtk_handle_box_new();
809 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
810 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
811 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
813 gtk_container_add(GTK_CONTAINER(hb), tb);
814 gtk_widget_show(GTK_WIDGET(tb));
816 sigc::connection* conn = new sigc::connection;
817 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
819 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
820 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
822 return hb;
823 }
825 GtkWidget *
826 sp_aux_toolbox_new()
827 {
828 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
830 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
832 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
834 gtk_widget_set_sensitive(tb, FALSE);
836 GtkWidget *hb = gtk_handle_box_new();
837 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
838 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
839 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
841 gtk_container_add(GTK_CONTAINER(hb), tb);
842 gtk_widget_show(GTK_WIDGET(tb));
844 sigc::connection* conn = new sigc::connection;
845 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
847 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
848 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
850 return hb;
851 }
853 //####################################
854 //# Commands Bar
855 //####################################
857 GtkWidget *
858 sp_commands_toolbox_new()
859 {
860 GtkWidget *tb = gtk_toolbar_new();
862 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
863 gtk_widget_set_sensitive(tb, FALSE);
865 GtkWidget *hb = gtk_handle_box_new();
866 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
867 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
868 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
870 gtk_container_add(GTK_CONTAINER(hb), tb);
871 gtk_widget_show(GTK_WIDGET(tb));
873 sigc::connection* conn = new sigc::connection;
874 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
876 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
877 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
879 return hb;
880 }
883 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
884 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
885 gchar const *path, gchar const *data, gdouble def,
886 GtkWidget *focusTarget,
887 GtkWidget *us,
888 GObject *dataKludge,
889 gboolean altx, gchar const *altx_mark,
890 gdouble lower, gdouble upper, gdouble step, gdouble page,
891 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
892 void (*callback)(GtkAdjustment *, GObject *),
893 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
894 {
895 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
896 lower, upper, step, page, page ) );
897 if (us) {
898 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
899 }
901 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
903 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
904 if ( shortLabel ) {
905 g_object_set( act, "short_label", shortLabel, NULL );
906 }
908 if ( (descrCount > 0) && descrLabels && descrValues ) {
909 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
910 }
912 if ( focusTarget ) {
913 ege_adjustment_action_set_focuswidget( act, focusTarget );
914 }
916 if ( altx && altx_mark ) {
917 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
918 }
920 if ( dataKludge ) {
921 g_object_set_data( dataKludge, data, adj );
922 }
924 // Using a cast just to make sure we pass in the right kind of function pointer
925 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
927 return act;
928 }
931 //####################################
932 //# node editing callbacks
933 //####################################
935 /**
936 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
937 */
938 static ShapeEditor *get_current_shape_editor()
939 {
940 if (!SP_ACTIVE_DESKTOP) {
941 return NULL;
942 }
944 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
946 if (!SP_IS_NODE_CONTEXT(event_context)) {
947 return NULL;
948 }
950 return SP_NODE_CONTEXT(event_context)->shape_editor;
951 }
954 void
955 sp_node_path_edit_add(void)
956 {
957 ShapeEditor *shape_editor = get_current_shape_editor();
958 if (shape_editor) shape_editor->add_node();
959 }
961 void
962 sp_node_path_edit_delete(void)
963 {
964 ShapeEditor *shape_editor = get_current_shape_editor();
965 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
966 }
968 void
969 sp_node_path_edit_delete_segment(void)
970 {
971 ShapeEditor *shape_editor = get_current_shape_editor();
972 if (shape_editor) shape_editor->delete_segment();
973 }
975 void
976 sp_node_path_edit_break(void)
977 {
978 ShapeEditor *shape_editor = get_current_shape_editor();
979 if (shape_editor) shape_editor->break_at_nodes();
980 }
982 void
983 sp_node_path_edit_join(void)
984 {
985 ShapeEditor *shape_editor = get_current_shape_editor();
986 if (shape_editor) shape_editor->join_nodes();
987 }
989 void
990 sp_node_path_edit_join_segment(void)
991 {
992 ShapeEditor *shape_editor = get_current_shape_editor();
993 if (shape_editor) shape_editor->join_segments();
994 }
996 void
997 sp_node_path_edit_toline(void)
998 {
999 ShapeEditor *shape_editor = get_current_shape_editor();
1000 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1001 }
1003 void
1004 sp_node_path_edit_tocurve(void)
1005 {
1006 ShapeEditor *shape_editor = get_current_shape_editor();
1007 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1008 }
1010 void
1011 sp_node_path_edit_cusp(void)
1012 {
1013 ShapeEditor *shape_editor = get_current_shape_editor();
1014 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1015 }
1017 void
1018 sp_node_path_edit_smooth(void)
1019 {
1020 ShapeEditor *shape_editor = get_current_shape_editor();
1021 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1022 }
1024 void
1025 sp_node_path_edit_symmetrical(void)
1026 {
1027 ShapeEditor *shape_editor = get_current_shape_editor();
1028 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1029 }
1031 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1032 bool show = gtk_toggle_action_get_active( act );
1033 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
1034 ShapeEditor *shape_editor = get_current_shape_editor();
1035 if (shape_editor) shape_editor->show_handles(show);
1036 }
1038 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1039 bool show = gtk_toggle_action_get_active( act );
1040 prefs_set_int_attribute ("tools.nodes", "show_helperpath", show ? 1 : 0);
1041 ShapeEditor *shape_editor = get_current_shape_editor();
1042 if (shape_editor) shape_editor->show_helperpath(show);
1043 }
1045 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1046 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1047 }
1049 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1050 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1051 }
1053 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1054 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1055 }
1057 /* is called when the node selection is modified */
1058 static void
1059 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1060 {
1061 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1062 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1063 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1064 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1066 // quit if run by the attr_changed listener
1067 if (g_object_get_data( tbl, "freeze" )) {
1068 return;
1069 }
1071 // in turn, prevent listener from responding
1072 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1074 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1075 SPUnit const *unit = tracker->getActiveUnit();
1077 ShapeEditor *shape_editor = get_current_shape_editor();
1078 if (shape_editor && shape_editor->has_nodepath()) {
1079 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1080 int n_selected = 0;
1081 if (nodepath) {
1082 n_selected = nodepath->numSelected();
1083 }
1085 if (n_selected == 0) {
1086 gtk_action_set_sensitive(xact, FALSE);
1087 gtk_action_set_sensitive(yact, FALSE);
1088 } else {
1089 gtk_action_set_sensitive(xact, TRUE);
1090 gtk_action_set_sensitive(yact, TRUE);
1091 NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1092 NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1094 if (n_selected == 1) {
1095 NR::Point sel_node = nodepath->singleSelectedCoords();
1096 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
1097 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
1098 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
1099 }
1100 } else {
1101 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
1102 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
1103 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1104 /* Note: Currently x and y will always have a value, even if the coordinates of the
1105 selected nodes don't coincide (in this case we use the coordinates of the center
1106 of the bounding box). So the entries are never set to zero. */
1107 // FIXME: Maybe we should clear the entry if several nodes are selected
1108 // instead of providing a kind of average value
1109 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1110 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1111 }
1112 }
1113 }
1114 } else {
1115 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1116 gtk_action_set_sensitive(xact, FALSE);
1117 gtk_action_set_sensitive(yact, FALSE);
1118 }
1120 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1121 }
1123 static void
1124 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1125 {
1126 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1128 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1129 SPUnit const *unit = tracker->getActiveUnit();
1131 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1132 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1133 }
1135 // quit if run by the attr_changed listener
1136 if (g_object_get_data( tbl, "freeze" )) {
1137 return;
1138 }
1140 // in turn, prevent listener from responding
1141 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1143 ShapeEditor *shape_editor = get_current_shape_editor();
1144 if (shape_editor && shape_editor->has_nodepath()) {
1145 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1146 if (!strcmp(value_name, "x")) {
1147 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
1148 }
1149 if (!strcmp(value_name, "y")) {
1150 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
1151 }
1152 }
1154 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1155 }
1157 static void
1158 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1159 {
1160 sp_node_path_value_changed(adj, tbl, "x");
1161 }
1163 static void
1164 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1165 {
1166 sp_node_path_value_changed(adj, tbl, "y");
1167 }
1169 void
1170 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1171 {
1172 {
1173 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1174 SPItem *item = selection->singleItem();
1175 if (item && SP_IS_LPE_ITEM(item)) {
1176 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1177 gtk_action_set_sensitive(w, TRUE);
1178 } else {
1179 gtk_action_set_sensitive(w, FALSE);
1180 }
1181 } else {
1182 gtk_action_set_sensitive(w, FALSE);
1183 }
1184 }
1186 {
1187 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1188 SPItem *item = selection->singleItem();
1189 if (item && item->clip_ref && item->clip_ref->getObject()) {
1190 gtk_action_set_sensitive(w, TRUE);
1191 } else {
1192 gtk_action_set_sensitive(w, FALSE);
1193 }
1194 }
1196 {
1197 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1198 SPItem *item = selection->singleItem();
1199 if (item && item->mask_ref && item->mask_ref->getObject()) {
1200 gtk_action_set_sensitive(w, TRUE);
1201 } else {
1202 gtk_action_set_sensitive(w, FALSE);
1203 }
1204 }
1205 }
1207 void
1208 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1209 {
1210 sp_node_toolbox_sel_changed (selection, tbl);
1211 }
1215 //################################
1216 //## Node Editing Toolbox ##
1217 //################################
1219 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1220 {
1221 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1222 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1223 g_object_set_data( holder, "tracker", tracker );
1225 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1227 {
1228 InkAction* inky = ink_action_new( "NodeInsertAction",
1229 _("Insert node"),
1230 _("Insert new nodes into selected segments"),
1231 "node_insert",
1232 secondarySize );
1233 g_object_set( inky, "short_label", _("Insert"), NULL );
1234 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1235 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1236 }
1238 {
1239 InkAction* inky = ink_action_new( "NodeDeleteAction",
1240 _("Delete node"),
1241 _("Delete selected nodes"),
1242 "node_delete",
1243 secondarySize );
1244 g_object_set( inky, "short_label", _("Delete"), NULL );
1245 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1246 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1247 }
1249 {
1250 InkAction* inky = ink_action_new( "NodeJoinAction",
1251 _("Join endnodes"),
1252 _("Join selected endnodes"),
1253 "node_join",
1254 secondarySize );
1255 g_object_set( inky, "short_label", _("Join"), NULL );
1256 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1257 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1258 }
1260 {
1261 InkAction* inky = ink_action_new( "NodeBreakAction",
1262 _("Break nodes"),
1263 _("Break path at selected nodes"),
1264 "node_break",
1265 secondarySize );
1266 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1267 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1268 }
1271 {
1272 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1273 _("Join with segment"),
1274 _("Join selected endnodes with a new segment"),
1275 "node_join_segment",
1276 secondarySize );
1277 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1278 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1279 }
1281 {
1282 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1283 _("Delete segment"),
1284 _("Delete segment between two non-endpoint nodes"),
1285 "node_delete_segment",
1286 secondarySize );
1287 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1288 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1289 }
1291 {
1292 InkAction* inky = ink_action_new( "NodeCuspAction",
1293 _("Node Cusp"),
1294 _("Make selected nodes corner"),
1295 "node_cusp",
1296 secondarySize );
1297 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1298 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1299 }
1301 {
1302 InkAction* inky = ink_action_new( "NodeSmoothAction",
1303 _("Node Smooth"),
1304 _("Make selected nodes smooth"),
1305 "node_smooth",
1306 secondarySize );
1307 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1308 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1309 }
1311 {
1312 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1313 _("Node Symmetric"),
1314 _("Make selected nodes symmetric"),
1315 "node_symmetric",
1316 secondarySize );
1317 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1318 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1319 }
1321 {
1322 InkAction* inky = ink_action_new( "NodeLineAction",
1323 _("Node Line"),
1324 _("Make selected segments lines"),
1325 "node_line",
1326 secondarySize );
1327 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1328 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1329 }
1331 {
1332 InkAction* inky = ink_action_new( "NodeCurveAction",
1333 _("Node Curve"),
1334 _("Make selected segments curves"),
1335 "node_curve",
1336 secondarySize );
1337 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1338 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1339 }
1341 {
1342 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1343 _("Show Handles"),
1344 _("Show the Bezier handles of selected nodes"),
1345 "nodes_show_handles",
1346 Inkscape::ICON_SIZE_DECORATION );
1347 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1348 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1349 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1350 }
1352 {
1353 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1354 _("Show Outline"),
1355 _("Show the outline of the path"),
1356 "nodes_show_helperpath",
1357 Inkscape::ICON_SIZE_DECORATION );
1358 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1359 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1360 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1361 }
1363 {
1364 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1365 _("Next path effect parameter"),
1366 _("Show next path effect parameter for editing"),
1367 "edit_next_parameter",
1368 Inkscape::ICON_SIZE_DECORATION );
1369 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1370 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1371 g_object_set_data( holder, "nodes_lpeedit", inky);
1372 }
1374 {
1375 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1376 _("Edit clipping path"),
1377 _("Edit the clipping path of the object"),
1378 "nodeedit-clippath",
1379 Inkscape::ICON_SIZE_DECORATION );
1380 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1381 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1382 g_object_set_data( holder, "nodes_clippathedit", inky);
1383 }
1385 {
1386 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1387 _("Edit mask path"),
1388 _("Edit the mask of the object"),
1389 "nodeedit-mask",
1390 Inkscape::ICON_SIZE_DECORATION );
1391 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1392 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1393 g_object_set_data( holder, "nodes_maskedit", inky);
1394 }
1396 /* X coord of selected node(s) */
1397 {
1398 EgeAdjustmentAction* eact = 0;
1399 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1400 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1401 eact = create_adjustment_action( "NodeXAction",
1402 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1403 "tools.nodes", "Xcoord", 0,
1404 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1405 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1406 labels, values, G_N_ELEMENTS(labels),
1407 sp_node_path_x_value_changed );
1408 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1409 g_object_set_data( holder, "nodes_x_action", eact );
1410 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1411 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1412 }
1414 /* Y coord of selected node(s) */
1415 {
1416 EgeAdjustmentAction* eact = 0;
1417 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1418 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1419 eact = create_adjustment_action( "NodeYAction",
1420 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1421 "tools.nodes", "Ycoord", 0,
1422 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1423 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1424 labels, values, G_N_ELEMENTS(labels),
1425 sp_node_path_y_value_changed );
1426 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1427 g_object_set_data( holder, "nodes_y_action", eact );
1428 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1429 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1430 }
1432 // add the units menu
1433 {
1434 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1435 gtk_action_group_add_action( mainActions, act );
1436 }
1439 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1441 //watch selection
1442 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1444 sigc::connection *c_selection_changed =
1445 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1446 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1447 pool->add_connection ("selection-changed", c_selection_changed);
1449 sigc::connection *c_selection_modified =
1450 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1451 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1452 pool->add_connection ("selection-modified", c_selection_modified);
1454 sigc::connection *c_subselection_changed =
1455 new sigc::connection (desktop->connectToolSubselectionChanged
1456 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1457 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1459 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1461 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1462 } // end of sp_node_toolbox_prep()
1465 //########################
1466 //## Zoom Toolbox ##
1467 //########################
1469 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1470 {
1471 // no custom GtkAction setup needed
1472 } // end of sp_zoom_toolbox_prep()
1474 void
1475 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1476 {
1477 toolbox_set_desktop(toolbox,
1478 desktop,
1479 setup_tool_toolbox,
1480 update_tool_toolbox,
1481 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1482 "event_context_connection")));
1483 }
1486 void
1487 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1488 {
1489 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1490 desktop,
1491 setup_aux_toolbox,
1492 update_aux_toolbox,
1493 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1494 "event_context_connection")));
1495 }
1497 void
1498 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1499 {
1500 toolbox_set_desktop(toolbox,
1501 desktop,
1502 setup_commands_toolbox,
1503 update_commands_toolbox,
1504 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1505 "event_context_connection")));
1506 }
1508 static void
1509 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1510 {
1511 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1512 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1514 if (old_desktop) {
1515 GList *children, *iter;
1517 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1518 for ( iter = children ; iter ; iter = iter->next ) {
1519 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1520 }
1521 g_list_free(children);
1522 }
1524 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1526 if (desktop) {
1527 gtk_widget_set_sensitive(toolbox, TRUE);
1528 setup_func(toolbox, desktop);
1529 update_func(desktop, desktop->event_context, toolbox);
1530 *conn = desktop->connectEventContextChanged
1531 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1532 } else {
1533 gtk_widget_set_sensitive(toolbox, FALSE);
1534 }
1536 } // end of toolbox_set_desktop()
1539 static void
1540 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1541 {
1542 gchar const * descr =
1543 "<ui>"
1544 " <toolbar name='ToolToolbar'>"
1545 " <toolitem action='ToolSelector' />"
1546 " <toolitem action='ToolNode' />"
1547 " <toolitem action='ToolTweak' />"
1548 " <toolitem action='ToolZoom' />"
1549 " <toolitem action='ToolRect' />"
1550 " <toolitem action='Tool3DBox' />"
1551 " <toolitem action='ToolArc' />"
1552 " <toolitem action='ToolStar' />"
1553 " <toolitem action='ToolSpiral' />"
1554 " <toolitem action='ToolPencil' />"
1555 " <toolitem action='ToolPen' />"
1556 " <toolitem action='ToolCalligraphic' />"
1557 " <toolitem action='ToolEraser' />"
1558 " <toolitem action='ToolPaintBucket' />"
1559 " <toolitem action='ToolText' />"
1560 " <toolitem action='ToolConnector' />"
1561 " <toolitem action='ToolGradient' />"
1562 " <toolitem action='ToolDropper' />"
1563 " </toolbar>"
1564 "</ui>";
1565 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1566 GtkUIManager* mgr = gtk_ui_manager_new();
1567 GError* errVal = 0;
1569 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1570 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1572 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1573 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1574 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1575 }
1576 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1577 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1579 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1580 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1582 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1584 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1585 if ( child ) {
1586 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1587 }
1589 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1590 // Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1591 }
1594 static void
1595 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox )
1596 {
1597 gchar const *const tname = ( eventcontext
1598 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1599 : NULL );
1600 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1602 for (int i = 0 ; tools[i].type_name ; i++ ) {
1603 Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1604 if ( act ) {
1605 bool setActive = tname && !strcmp(tname, tools[i].type_name);
1606 Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1607 if ( verbAct ) {
1608 verbAct->set_active(setActive);
1609 }
1610 }
1611 }
1612 }
1614 static void
1615 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1616 {
1617 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1618 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1619 GtkUIManager* mgr = gtk_ui_manager_new();
1620 GError* errVal = 0;
1621 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1622 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1624 std::map<std::string, GtkWidget*> dataHolders;
1626 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1627 if ( aux_toolboxes[i].prep_func ) {
1628 // converted to GtkActions and UIManager
1630 GtkWidget* kludge = gtk_toolbar_new();
1631 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1632 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1633 dataHolders[aux_toolboxes[i].type_name] = kludge;
1634 aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1635 } else {
1637 GtkWidget *sub_toolbox = 0;
1638 if (aux_toolboxes[i].create_func == NULL)
1639 sub_toolbox = sp_empty_toolbox_new(desktop);
1640 else {
1641 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1642 }
1644 gtk_size_group_add_widget( grouper, sub_toolbox );
1646 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1647 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1649 }
1650 }
1652 // Second pass to create toolbars *after* all GtkActions are created
1653 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1654 if ( aux_toolboxes[i].prep_func ) {
1655 // converted to GtkActions and UIManager
1657 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1659 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1660 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1662 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1663 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1664 g_free( tmp );
1665 tmp = 0;
1667 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1668 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1669 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1670 }
1671 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1674 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1676 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1677 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1678 swatch->setDesktop( desktop );
1679 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1680 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1681 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1682 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 );
1683 }
1685 gtk_widget_show_all( holder );
1686 sp_set_font_size_smaller( holder );
1688 gtk_size_group_add_widget( grouper, holder );
1690 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1691 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1692 }
1693 }
1695 g_object_unref( G_OBJECT(grouper) );
1696 }
1698 static void
1699 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1700 {
1701 gchar const *tname = ( eventcontext
1702 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1703 : NULL );
1704 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1705 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1706 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1707 gtk_widget_show_all(sub_toolbox);
1708 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1709 } else {
1710 gtk_widget_hide(sub_toolbox);
1711 }
1712 }
1713 }
1715 static void
1716 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1717 {
1718 gchar const * descr =
1719 "<ui>"
1720 " <toolbar name='CommandsToolbar'>"
1721 " <toolitem action='FileNew' />"
1722 " <toolitem action='FileOpen' />"
1723 " <toolitem action='FileSave' />"
1724 " <toolitem action='FilePrint' />"
1725 " <separator />"
1726 " <toolitem action='FileImport' />"
1727 " <toolitem action='FileExport' />"
1728 " <separator />"
1729 " <toolitem action='EditUndo' />"
1730 " <toolitem action='EditRedo' />"
1731 " <separator />"
1732 " <toolitem action='EditCopy' />"
1733 " <toolitem action='EditCut' />"
1734 " <toolitem action='EditPaste' />"
1735 " <separator />"
1736 " <toolitem action='ZoomSelection' />"
1737 " <toolitem action='ZoomDrawing' />"
1738 " <toolitem action='ZoomPage' />"
1739 " <separator />"
1740 " <toolitem action='EditDuplicate' />"
1741 " <toolitem action='EditClone' />"
1742 " <toolitem action='EditUnlinkClone' />"
1743 " <separator />"
1744 " <toolitem action='SelectionGroup' />"
1745 " <toolitem action='SelectionUnGroup' />"
1746 " <separator />"
1747 " <toolitem action='DialogFillStroke' />"
1748 " <toolitem action='DialogText' />"
1749 " <toolitem action='DialogXMLEditor' />"
1750 " <toolitem action='DialogAlignDistribute' />"
1751 " <separator />"
1752 " <toolitem action='DialogPreferences' />"
1753 " <toolitem action='DialogDocumentProperties' />"
1754 " </toolbar>"
1755 "</ui>";
1756 Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1759 GtkUIManager* mgr = gtk_ui_manager_new();
1760 GError* errVal = 0;
1762 gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1763 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1765 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1766 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1767 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1768 }
1770 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1771 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1773 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1774 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1777 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1779 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1780 if ( child ) {
1781 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1782 }
1784 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1785 }
1787 static void
1788 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1789 {
1790 }
1792 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1793 {
1794 gtk_widget_show(toolbox_toplevel);
1795 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1797 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1798 if (!shown_toolbox) {
1799 return;
1800 }
1801 gtk_widget_show(toolbox);
1803 gtk_widget_show_all(shown_toolbox);
1804 }
1806 static GtkWidget *
1807 sp_empty_toolbox_new(SPDesktop *desktop)
1808 {
1809 GtkWidget *tbl = gtk_toolbar_new();
1810 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1811 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1813 gtk_widget_show_all(tbl);
1814 sp_set_font_size_smaller (tbl);
1816 return tbl;
1817 }
1819 #define MODE_LABEL_WIDTH 70
1821 //########################
1822 //## Star ##
1823 //########################
1825 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1826 {
1827 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1829 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1830 // do not remember prefs if this call is initiated by an undo change, because undoing object
1831 // creation sets bogus values to its attributes before it is deleted
1832 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1833 }
1835 // quit if run by the attr_changed listener
1836 if (g_object_get_data( dataKludge, "freeze" )) {
1837 return;
1838 }
1840 // in turn, prevent listener from responding
1841 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1843 bool modmade = false;
1845 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1846 GSList const *items = selection->itemList();
1847 for (; items != NULL; items = items->next) {
1848 if (SP_IS_STAR((SPItem *) items->data)) {
1849 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1850 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1851 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1852 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1853 + M_PI / (gint)adj->value));
1854 SP_OBJECT((SPItem *) items->data)->updateRepr();
1855 modmade = true;
1856 }
1857 }
1858 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1859 _("Star: Change number of corners"));
1861 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1862 }
1864 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1865 {
1866 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1868 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1869 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1870 }
1872 // quit if run by the attr_changed listener
1873 if (g_object_get_data( dataKludge, "freeze" )) {
1874 return;
1875 }
1877 // in turn, prevent listener from responding
1878 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1880 bool modmade = false;
1881 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1882 GSList const *items = selection->itemList();
1883 for (; items != NULL; items = items->next) {
1884 if (SP_IS_STAR((SPItem *) items->data)) {
1885 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1887 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1888 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1889 if (r2 < r1) {
1890 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1891 } else {
1892 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1893 }
1895 SP_OBJECT((SPItem *) items->data)->updateRepr();
1896 modmade = true;
1897 }
1898 }
1900 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1901 _("Star: Change spoke ratio"));
1903 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1904 }
1906 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1907 {
1908 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1909 bool flat = ege_select_one_action_get_active( act ) == 0;
1911 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1912 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1913 flat ? "true" : "false" );
1914 }
1916 // quit if run by the attr_changed listener
1917 if (g_object_get_data( dataKludge, "freeze" )) {
1918 return;
1919 }
1921 // in turn, prevent listener from responding
1922 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1924 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1925 GSList const *items = selection->itemList();
1926 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1927 bool modmade = false;
1929 if ( prop_action ) {
1930 gtk_action_set_sensitive( prop_action, !flat );
1931 }
1933 for (; items != NULL; items = items->next) {
1934 if (SP_IS_STAR((SPItem *) items->data)) {
1935 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1936 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1937 SP_OBJECT((SPItem *) items->data)->updateRepr();
1938 modmade = true;
1939 }
1940 }
1942 if (modmade) {
1943 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1944 flat ? _("Make polygon") : _("Make star"));
1945 }
1947 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1948 }
1950 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1951 {
1952 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1954 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1955 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1956 }
1958 // quit if run by the attr_changed listener
1959 if (g_object_get_data( dataKludge, "freeze" )) {
1960 return;
1961 }
1963 // in turn, prevent listener from responding
1964 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1966 bool modmade = false;
1968 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1969 GSList const *items = selection->itemList();
1970 for (; items != NULL; items = items->next) {
1971 if (SP_IS_STAR((SPItem *) items->data)) {
1972 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1973 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1974 SP_OBJECT(items->data)->updateRepr();
1975 modmade = true;
1976 }
1977 }
1978 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1979 _("Star: Change rounding"));
1981 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1982 }
1984 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1985 {
1986 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1988 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1989 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1990 }
1992 // quit if run by the attr_changed listener
1993 if (g_object_get_data( dataKludge, "freeze" )) {
1994 return;
1995 }
1997 // in turn, prevent listener from responding
1998 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2000 bool modmade = false;
2002 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2003 GSList const *items = selection->itemList();
2004 for (; items != NULL; items = items->next) {
2005 if (SP_IS_STAR((SPItem *) items->data)) {
2006 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2007 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2008 SP_OBJECT(items->data)->updateRepr();
2009 modmade = true;
2010 }
2011 }
2012 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2013 _("Star: Change randomization"));
2015 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2016 }
2019 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2020 gchar const */*old_value*/, gchar const */*new_value*/,
2021 bool /*is_interactive*/, gpointer data)
2022 {
2023 GtkWidget *tbl = GTK_WIDGET(data);
2025 // quit if run by the _changed callbacks
2026 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2027 return;
2028 }
2030 // in turn, prevent callbacks from responding
2031 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2033 GtkAdjustment *adj = 0;
2035 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2036 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2038 if (!strcmp(name, "inkscape:randomized")) {
2039 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2040 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2041 } else if (!strcmp(name, "inkscape:rounded")) {
2042 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2043 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2044 } else if (!strcmp(name, "inkscape:flatsided")) {
2045 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2046 char const *flatsides = repr->attribute("inkscape:flatsided");
2047 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2048 if ( flatsides && !strcmp(flatsides,"false") ) {
2049 ege_select_one_action_set_active( flat_action, 1 );
2050 gtk_action_set_sensitive( prop_action, TRUE );
2051 } else {
2052 ege_select_one_action_set_active( flat_action, 0 );
2053 gtk_action_set_sensitive( prop_action, FALSE );
2054 }
2055 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2056 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2057 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2058 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2059 if (r2 < r1) {
2060 gtk_adjustment_set_value(adj, r2/r1);
2061 } else {
2062 gtk_adjustment_set_value(adj, r1/r2);
2063 }
2064 } else if (!strcmp(name, "sodipodi:sides")) {
2065 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2066 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2067 }
2069 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2070 }
2073 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2074 {
2075 NULL, /* child_added */
2076 NULL, /* child_removed */
2077 star_tb_event_attr_changed,
2078 NULL, /* content_changed */
2079 NULL /* order_changed */
2080 };
2083 /**
2084 * \param selection Should not be NULL.
2085 */
2086 static void
2087 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2088 {
2089 int n_selected = 0;
2090 Inkscape::XML::Node *repr = NULL;
2092 purge_repr_listener( tbl, tbl );
2094 for (GSList const *items = selection->itemList();
2095 items != NULL;
2096 items = items->next)
2097 {
2098 if (SP_IS_STAR((SPItem *) items->data)) {
2099 n_selected++;
2100 repr = SP_OBJECT_REPR((SPItem *) items->data);
2101 }
2102 }
2104 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2106 if (n_selected == 0) {
2107 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2108 } else if (n_selected == 1) {
2109 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2111 if (repr) {
2112 g_object_set_data( tbl, "repr", repr );
2113 Inkscape::GC::anchor(repr);
2114 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2115 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2116 }
2117 } else {
2118 // FIXME: implement averaging of all parameters for multiple selected stars
2119 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2120 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2121 }
2122 }
2125 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2126 {
2127 // FIXME: in this and all other _default functions, set some flag telling the value_changed
2128 // callbacks to lump all the changes for all selected objects in one undo step
2130 GtkAdjustment *adj = 0;
2132 // fixme: make settable in prefs!
2133 gint mag = 5;
2134 gdouble prop = 0.5;
2135 gboolean flat = FALSE;
2136 gdouble randomized = 0;
2137 gdouble rounded = 0;
2139 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2140 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2142 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2143 gtk_action_set_sensitive( sb2, !flat );
2145 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2146 gtk_adjustment_set_value(adj, mag);
2147 gtk_adjustment_value_changed(adj);
2149 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2150 gtk_adjustment_set_value(adj, prop);
2151 gtk_adjustment_value_changed(adj);
2153 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2154 gtk_adjustment_set_value(adj, rounded);
2155 gtk_adjustment_value_changed(adj);
2157 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2158 gtk_adjustment_set_value(adj, randomized);
2159 gtk_adjustment_value_changed(adj);
2160 }
2163 void
2164 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2165 {
2166 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2167 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2168 GtkWidget *l = gtk_label_new(NULL);
2169 gtk_label_set_markup(GTK_LABEL(l), title);
2170 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2171 if ( GTK_IS_TOOLBAR(tbl) ) {
2172 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2173 } else {
2174 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2175 }
2176 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2177 }
2180 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2181 {
2182 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2184 {
2185 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2186 ege_output_action_set_use_markup( act, TRUE );
2187 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2188 g_object_set_data( holder, "mode_action", act );
2189 }
2191 {
2192 EgeAdjustmentAction* eact = 0;
2193 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2194 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2196 /* Flatsided checkbox */
2197 {
2198 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2200 GtkTreeIter iter;
2201 gtk_list_store_append( model, &iter );
2202 gtk_list_store_set( model, &iter,
2203 0, _("Polygon"),
2204 1, _("Regular polygon (with one handle) instead of a star"),
2205 2, "star_flat",
2206 -1 );
2208 gtk_list_store_append( model, &iter );
2209 gtk_list_store_set( model, &iter,
2210 0, _("Star"),
2211 1, _("Star instead of a regular polygon (with one handle)"),
2212 2, "star_angled",
2213 -1 );
2215 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2216 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2217 g_object_set_data( holder, "flat_action", act );
2219 ege_select_one_action_set_appearance( act, "full" );
2220 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2221 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2222 ege_select_one_action_set_icon_column( act, 2 );
2223 ege_select_one_action_set_icon_size( act, secondarySize );
2224 ege_select_one_action_set_tooltip_column( act, 1 );
2226 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2227 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2228 }
2230 /* Magnitude */
2231 {
2232 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2233 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2234 eact = create_adjustment_action( "MagnitudeAction",
2235 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2236 "tools.shapes.star", "magnitude", 3,
2237 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2238 3, 1024, 1, 5,
2239 labels, values, G_N_ELEMENTS(labels),
2240 sp_stb_magnitude_value_changed,
2241 1.0, 0 );
2242 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2243 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2244 }
2246 /* Spoke ratio */
2247 {
2248 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2249 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2250 eact = create_adjustment_action( "SpokeAction",
2251 _("Spoke ratio"), _("Spoke ratio:"),
2252 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2253 // Base radius is the same for the closest handle.
2254 _("Base radius to tip radius ratio"),
2255 "tools.shapes.star", "proportion", 0.5,
2256 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2257 0.01, 1.0, 0.01, 0.1,
2258 labels, values, G_N_ELEMENTS(labels),
2259 sp_stb_proportion_value_changed );
2260 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2261 g_object_set_data( holder, "prop_action", eact );
2262 }
2264 if ( !isFlatSided ) {
2265 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2266 } else {
2267 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2268 }
2270 /* Roundedness */
2271 {
2272 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2273 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2274 eact = create_adjustment_action( "RoundednessAction",
2275 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2276 "tools.shapes.star", "rounded", 0.0,
2277 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2278 -10.0, 10.0, 0.01, 0.1,
2279 labels, values, G_N_ELEMENTS(labels),
2280 sp_stb_rounded_value_changed );
2281 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2282 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2283 }
2285 /* Randomization */
2286 {
2287 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2288 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2289 eact = create_adjustment_action( "RandomizationAction",
2290 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2291 "tools.shapes.star", "randomized", 0.0,
2292 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2293 -10.0, 10.0, 0.001, 0.01,
2294 labels, values, G_N_ELEMENTS(labels),
2295 sp_stb_randomized_value_changed, 0.1, 3 );
2296 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2297 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2298 }
2299 }
2301 {
2302 /* Reset */
2303 {
2304 GtkAction* act = gtk_action_new( "StarResetAction",
2305 _("Defaults"),
2306 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2307 GTK_STOCK_CLEAR );
2308 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2309 gtk_action_group_add_action( mainActions, act );
2310 gtk_action_set_sensitive( act, TRUE );
2311 }
2312 }
2314 sigc::connection *connection = new sigc::connection(
2315 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2316 );
2317 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2318 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2319 }
2322 //########################
2323 //## Rect ##
2324 //########################
2326 static void sp_rtb_sensitivize( GObject *tbl )
2327 {
2328 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2329 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2330 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2332 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2333 gtk_action_set_sensitive( not_rounded, FALSE );
2334 } else {
2335 gtk_action_set_sensitive( not_rounded, TRUE );
2336 }
2337 }
2340 static void
2341 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2342 void (*setter)(SPRect *, gdouble))
2343 {
2344 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2346 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2347 SPUnit const *unit = tracker->getActiveUnit();
2349 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2350 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2351 }
2353 // quit if run by the attr_changed listener
2354 if (g_object_get_data( tbl, "freeze" )) {
2355 return;
2356 }
2358 // in turn, prevent listener from responding
2359 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2361 bool modmade = false;
2362 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2363 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2364 if (SP_IS_RECT(items->data)) {
2365 if (adj->value != 0) {
2366 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2367 } else {
2368 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2369 }
2370 modmade = true;
2371 }
2372 }
2374 sp_rtb_sensitivize( tbl );
2376 if (modmade) {
2377 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2378 _("Change rectangle"));
2379 }
2381 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2382 }
2384 static void
2385 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2386 {
2387 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2388 }
2390 static void
2391 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2392 {
2393 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2394 }
2396 static void
2397 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2398 {
2399 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2400 }
2402 static void
2403 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2404 {
2405 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2406 }
2410 static void
2411 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2412 {
2413 GtkAdjustment *adj = 0;
2415 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2416 gtk_adjustment_set_value(adj, 0.0);
2417 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2418 gtk_adjustment_value_changed(adj);
2420 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2421 gtk_adjustment_set_value(adj, 0.0);
2422 gtk_adjustment_value_changed(adj);
2424 sp_rtb_sensitivize( obj );
2425 }
2427 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2428 gchar const */*old_value*/, gchar const */*new_value*/,
2429 bool /*is_interactive*/, gpointer data)
2430 {
2431 GObject *tbl = G_OBJECT(data);
2433 // quit if run by the _changed callbacks
2434 if (g_object_get_data( tbl, "freeze" )) {
2435 return;
2436 }
2438 // in turn, prevent callbacks from responding
2439 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2441 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2442 SPUnit const *unit = tracker->getActiveUnit();
2444 gpointer item = g_object_get_data( tbl, "item" );
2445 if (item && SP_IS_RECT(item)) {
2446 {
2447 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2448 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2449 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2450 }
2452 {
2453 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2454 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2455 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2456 }
2458 {
2459 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2460 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2461 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2462 }
2464 {
2465 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2466 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2467 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2468 }
2469 }
2471 sp_rtb_sensitivize( tbl );
2473 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2474 }
2477 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2478 NULL, /* child_added */
2479 NULL, /* child_removed */
2480 rect_tb_event_attr_changed,
2481 NULL, /* content_changed */
2482 NULL /* order_changed */
2483 };
2485 /**
2486 * \param selection should not be NULL.
2487 */
2488 static void
2489 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2490 {
2491 int n_selected = 0;
2492 Inkscape::XML::Node *repr = NULL;
2493 SPItem *item = NULL;
2495 if ( g_object_get_data( tbl, "repr" ) ) {
2496 g_object_set_data( tbl, "item", NULL );
2497 }
2498 purge_repr_listener( tbl, tbl );
2500 for (GSList const *items = selection->itemList();
2501 items != NULL;
2502 items = items->next) {
2503 if (SP_IS_RECT((SPItem *) items->data)) {
2504 n_selected++;
2505 item = (SPItem *) items->data;
2506 repr = SP_OBJECT_REPR(item);
2507 }
2508 }
2510 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2512 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2514 if (n_selected == 0) {
2515 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2517 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2518 gtk_action_set_sensitive(w, FALSE);
2519 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2520 gtk_action_set_sensitive(h, FALSE);
2522 } else if (n_selected == 1) {
2523 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2524 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2526 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2527 gtk_action_set_sensitive(w, TRUE);
2528 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2529 gtk_action_set_sensitive(h, TRUE);
2531 if (repr) {
2532 g_object_set_data( tbl, "repr", repr );
2533 g_object_set_data( tbl, "item", item );
2534 Inkscape::GC::anchor(repr);
2535 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2536 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2537 }
2538 } else {
2539 // FIXME: implement averaging of all parameters for multiple selected
2540 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2541 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2542 sp_rtb_sensitivize( tbl );
2543 }
2544 }
2547 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2548 {
2549 EgeAdjustmentAction* eact = 0;
2550 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2552 {
2553 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2554 ege_output_action_set_use_markup( act, TRUE );
2555 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2556 g_object_set_data( holder, "mode_action", act );
2557 }
2559 // rx/ry units menu: create
2560 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2561 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2562 // fixme: add % meaning per cent of the width/height
2563 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2564 g_object_set_data( holder, "tracker", tracker );
2566 /* W */
2567 {
2568 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2569 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2570 eact = create_adjustment_action( "RectWidthAction",
2571 _("Width"), _("W:"), _("Width of rectangle"),
2572 "tools.shapes.rect", "width", 0,
2573 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2574 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2575 labels, values, G_N_ELEMENTS(labels),
2576 sp_rtb_width_value_changed );
2577 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2578 g_object_set_data( holder, "width_action", eact );
2579 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2580 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2581 }
2583 /* H */
2584 {
2585 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2586 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2587 eact = create_adjustment_action( "RectHeightAction",
2588 _("Height"), _("H:"), _("Height of rectangle"),
2589 "tools.shapes.rect", "height", 0,
2590 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2591 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2592 labels, values, G_N_ELEMENTS(labels),
2593 sp_rtb_height_value_changed );
2594 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2595 g_object_set_data( holder, "height_action", eact );
2596 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2597 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2598 }
2600 /* rx */
2601 {
2602 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2603 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2604 eact = create_adjustment_action( "RadiusXAction",
2605 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2606 "tools.shapes.rect", "rx", 0,
2607 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2608 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2609 labels, values, G_N_ELEMENTS(labels),
2610 sp_rtb_rx_value_changed);
2611 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2612 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2613 }
2615 /* ry */
2616 {
2617 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2618 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2619 eact = create_adjustment_action( "RadiusYAction",
2620 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2621 "tools.shapes.rect", "ry", 0,
2622 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2623 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2624 labels, values, G_N_ELEMENTS(labels),
2625 sp_rtb_ry_value_changed);
2626 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2627 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2628 }
2630 // add the units menu
2631 {
2632 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2633 gtk_action_group_add_action( mainActions, act );
2634 }
2636 /* Reset */
2637 {
2638 InkAction* inky = ink_action_new( "RectResetAction",
2639 _("Not rounded"),
2640 _("Make corners sharp"),
2641 "squared_corner",
2642 secondarySize );
2643 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2644 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2645 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2646 g_object_set_data( holder, "not_rounded", inky );
2647 }
2649 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2650 sp_rtb_sensitivize( holder );
2652 sigc::connection *connection = new sigc::connection(
2653 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2654 );
2655 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2656 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2657 }
2659 //########################
2660 //## 3D Box ##
2661 //########################
2663 // normalize angle so that it lies in the interval [0,360]
2664 static double box3d_normalize_angle (double a) {
2665 double angle = a + ((int) (a/360.0))*360;
2666 if (angle < 0) {
2667 angle += 360.0;
2668 }
2669 return angle;
2670 }
2672 static void
2673 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2674 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2675 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2676 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2677 // are reset).
2678 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2680 if (is_infinite) {
2681 gtk_toggle_action_set_active(tact, TRUE);
2682 gtk_action_set_sensitive(act, TRUE);
2684 double angle = persp3d_get_infinite_angle(persp, axis);
2685 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2686 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2687 }
2688 } else {
2689 gtk_toggle_action_set_active(tact, FALSE);
2690 gtk_action_set_sensitive(act, FALSE);
2691 }
2692 }
2694 static void
2695 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2696 if (!persp_repr) {
2697 g_print ("No perspective given to box3d_resync_toolbar().\n");
2698 return;
2699 }
2701 GtkWidget *tbl = GTK_WIDGET(data);
2702 GtkAdjustment *adj = 0;
2703 GtkAction *act = 0;
2704 GtkToggleAction *tact = 0;
2705 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2706 {
2707 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2708 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2709 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2711 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2712 }
2713 {
2714 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2715 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2716 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2718 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2719 }
2720 {
2721 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2722 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2723 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2725 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2726 }
2727 }
2729 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2730 gchar const */*old_value*/, gchar const */*new_value*/,
2731 bool /*is_interactive*/, gpointer data)
2732 {
2733 GtkWidget *tbl = GTK_WIDGET(data);
2735 // quit if run by the attr_changed listener
2736 // note: it used to work without the differently called freeze_ attributes (here and in
2737 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2738 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2739 return;
2740 }
2742 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2743 // sp_document_maybe_done() when the document is undo insensitive)
2744 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2746 // TODO: Only update the appropriate part of the toolbar
2747 // if (!strcmp(name, "inkscape:vp_z")) {
2748 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2749 // }
2751 Persp3D *persp = persp3d_get_from_repr(repr);
2752 persp3d_update_box_reprs(persp);
2754 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2755 }
2757 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2758 {
2759 NULL, /* child_added */
2760 NULL, /* child_removed */
2761 box3d_persp_tb_event_attr_changed,
2762 NULL, /* content_changed */
2763 NULL /* order_changed */
2764 };
2766 /**
2767 * \param selection Should not be NULL.
2768 */
2769 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2770 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2771 static void
2772 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2773 {
2774 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2775 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2776 // update the perspectives with infinite VPs and leave the other ones untouched).
2778 Inkscape::XML::Node *persp_repr = NULL;
2779 purge_repr_listener(tbl, tbl);
2781 SPItem *item = selection->singleItem();
2782 if (item && SP_IS_BOX3D(item)) {
2783 // FIXME: Also deal with multiple selected boxes
2784 SPBox3D *box = SP_BOX3D(item);
2785 Persp3D *persp = box3d_get_perspective(box);
2786 persp_repr = SP_OBJECT_REPR(persp);
2787 if (persp_repr) {
2788 g_object_set_data(tbl, "repr", persp_repr);
2789 Inkscape::GC::anchor(persp_repr);
2790 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2791 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2792 }
2794 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2795 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2797 box3d_resync_toolbar(persp_repr, tbl);
2798 }
2799 }
2801 static void
2802 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2803 {
2804 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2805 SPDocument *document = sp_desktop_document(desktop);
2807 // quit if run by the attr_changed listener
2808 // note: it used to work without the differently called freeze_ attributes (here and in
2809 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2810 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2811 return;
2812 }
2814 // in turn, prevent listener from responding
2815 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2817 //Persp3D *persp = document->current_persp3d;
2818 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2819 if (sel_persps.empty()) {
2820 // this can happen when the document is created; we silently ignore it
2821 return;
2822 }
2823 Persp3D *persp = sel_persps.front();
2825 persp->tmat.set_infinite_direction (axis, adj->value);
2826 SP_OBJECT(persp)->updateRepr();
2828 // TODO: use the correct axis here, too
2829 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2831 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2832 }
2835 static void
2836 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2837 {
2838 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2839 }
2841 static void
2842 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2843 {
2844 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2845 }
2847 static void
2848 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2849 {
2850 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2851 }
2854 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2855 {
2856 // TODO: Take all selected perspectives into account
2857 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2858 if (sel_persps.empty()) {
2859 // this can happen when the document is created; we silently ignore it
2860 return;
2861 }
2862 Persp3D *persp = sel_persps.front();
2864 bool set_infinite = gtk_toggle_action_get_active(act);
2865 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2866 }
2868 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2869 {
2870 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2871 }
2873 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2874 {
2875 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2876 }
2878 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2879 {
2880 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2881 }
2883 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2884 {
2885 EgeAdjustmentAction* eact = 0;
2886 SPDocument *document = sp_desktop_document (desktop);
2887 Persp3D *persp = document->current_persp3d;
2889 EgeAdjustmentAction* box3d_angle_x = 0;
2890 EgeAdjustmentAction* box3d_angle_y = 0;
2891 EgeAdjustmentAction* box3d_angle_z = 0;
2893 /* Angle X */
2894 {
2895 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2896 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2897 eact = create_adjustment_action( "3DBoxAngleXAction",
2898 _("Angle in X direction"), _("Angle X:"),
2899 // Translators: PL is short for 'perspective line'
2900 _("Angle of PLs in X direction"),
2901 "tools.shapes.3dbox", "box3d_angle_x", 30,
2902 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2903 -360.0, 360.0, 1.0, 10.0,
2904 labels, values, G_N_ELEMENTS(labels),
2905 box3d_angle_x_value_changed );
2906 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2907 g_object_set_data( holder, "box3d_angle_x_action", eact );
2908 box3d_angle_x = eact;
2909 }
2911 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2912 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2913 } else {
2914 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2915 }
2918 /* VP X state */
2919 {
2920 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2921 // Translators: VP is short for 'vanishing point'
2922 _("State of VP in X direction"),
2923 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2924 "toggle_vp_x",
2925 Inkscape::ICON_SIZE_DECORATION );
2926 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2927 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2928 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2929 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2930 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2931 }
2933 /* Angle Y */
2934 {
2935 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2936 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2937 eact = create_adjustment_action( "3DBoxAngleYAction",
2938 _("Angle in Y direction"), _("Angle Y:"),
2939 // Translators: PL is short for 'perspective line'
2940 _("Angle of PLs in Y direction"),
2941 "tools.shapes.3dbox", "box3d_angle_y", 30,
2942 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2943 -360.0, 360.0, 1.0, 10.0,
2944 labels, values, G_N_ELEMENTS(labels),
2945 box3d_angle_y_value_changed );
2946 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2947 g_object_set_data( holder, "box3d_angle_y_action", eact );
2948 box3d_angle_y = eact;
2949 }
2951 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2952 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2953 } else {
2954 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2955 }
2957 /* VP Y state */
2958 {
2959 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2960 // Translators: VP is short for 'vanishing point'
2961 _("State of VP in Y direction"),
2962 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2963 "toggle_vp_y",
2964 Inkscape::ICON_SIZE_DECORATION );
2965 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2966 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2967 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2968 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2969 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2970 }
2972 /* Angle Z */
2973 {
2974 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2975 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2976 eact = create_adjustment_action( "3DBoxAngleZAction",
2977 _("Angle in Z direction"), _("Angle Z:"),
2978 // Translators: PL is short for 'perspective line'
2979 _("Angle of PLs in Z direction"),
2980 "tools.shapes.3dbox", "box3d_angle_z", 30,
2981 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2982 -360.0, 360.0, 1.0, 10.0,
2983 labels, values, G_N_ELEMENTS(labels),
2984 box3d_angle_z_value_changed );
2985 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2986 g_object_set_data( holder, "box3d_angle_z_action", eact );
2987 box3d_angle_z = eact;
2988 }
2990 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2991 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2992 } else {
2993 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2994 }
2996 /* VP Z state */
2997 {
2998 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2999 // Translators: VP is short for 'vanishing point'
3000 _("State of VP in Z direction"),
3001 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3002 "toggle_vp_z",
3003 Inkscape::ICON_SIZE_DECORATION );
3004 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3005 g_object_set_data( holder, "box3d_vp_z_state_action", act );
3006 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3007 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3008 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3009 }
3011 sigc::connection *connection = new sigc::connection(
3012 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3013 );
3014 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3015 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3016 }
3018 //########################
3019 //## Spiral ##
3020 //########################
3022 static void
3023 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3024 {
3025 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3027 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3028 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3029 }
3031 // quit if run by the attr_changed listener
3032 if (g_object_get_data( tbl, "freeze" )) {
3033 return;
3034 }
3036 // in turn, prevent listener from responding
3037 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3039 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3041 bool modmade = false;
3042 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3043 items != NULL;
3044 items = items->next)
3045 {
3046 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3047 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3048 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3049 SP_OBJECT((SPItem *) items->data)->updateRepr();
3050 modmade = true;
3051 }
3052 }
3054 g_free(namespaced_name);
3056 if (modmade) {
3057 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3058 _("Change spiral"));
3059 }
3061 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3062 }
3064 static void
3065 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3066 {
3067 sp_spl_tb_value_changed(adj, tbl, "revolution");
3068 }
3070 static void
3071 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3072 {
3073 sp_spl_tb_value_changed(adj, tbl, "expansion");
3074 }
3076 static void
3077 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3078 {
3079 sp_spl_tb_value_changed(adj, tbl, "t0");
3080 }
3082 static void
3083 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3084 {
3085 GtkWidget *tbl = GTK_WIDGET(obj);
3087 GtkAdjustment *adj;
3089 // fixme: make settable
3090 gdouble rev = 5;
3091 gdouble exp = 1.0;
3092 gdouble t0 = 0.0;
3094 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3095 gtk_adjustment_set_value(adj, rev);
3096 gtk_adjustment_value_changed(adj);
3098 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3099 gtk_adjustment_set_value(adj, exp);
3100 gtk_adjustment_value_changed(adj);
3102 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3103 gtk_adjustment_set_value(adj, t0);
3104 gtk_adjustment_value_changed(adj);
3106 spinbutton_defocus(GTK_OBJECT(tbl));
3107 }
3110 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3111 gchar const */*old_value*/, gchar const */*new_value*/,
3112 bool /*is_interactive*/, gpointer data)
3113 {
3114 GtkWidget *tbl = GTK_WIDGET(data);
3116 // quit if run by the _changed callbacks
3117 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3118 return;
3119 }
3121 // in turn, prevent callbacks from responding
3122 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3124 GtkAdjustment *adj;
3125 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3126 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3128 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3129 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3131 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3132 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3134 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3135 }
3138 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3139 NULL, /* child_added */
3140 NULL, /* child_removed */
3141 spiral_tb_event_attr_changed,
3142 NULL, /* content_changed */
3143 NULL /* order_changed */
3144 };
3146 static void
3147 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3148 {
3149 int n_selected = 0;
3150 Inkscape::XML::Node *repr = NULL;
3152 purge_repr_listener( tbl, tbl );
3154 for (GSList const *items = selection->itemList();
3155 items != NULL;
3156 items = items->next)
3157 {
3158 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3159 n_selected++;
3160 repr = SP_OBJECT_REPR((SPItem *) items->data);
3161 }
3162 }
3164 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3166 if (n_selected == 0) {
3167 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3168 } else if (n_selected == 1) {
3169 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3171 if (repr) {
3172 g_object_set_data( tbl, "repr", repr );
3173 Inkscape::GC::anchor(repr);
3174 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3175 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3176 }
3177 } else {
3178 // FIXME: implement averaging of all parameters for multiple selected
3179 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3180 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3181 }
3182 }
3185 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3186 {
3187 EgeAdjustmentAction* eact = 0;
3188 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3190 {
3191 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3192 ege_output_action_set_use_markup( act, TRUE );
3193 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3194 g_object_set_data( holder, "mode_action", act );
3195 }
3197 /* Revolution */
3198 {
3199 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3200 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3201 eact = create_adjustment_action( "SpiralRevolutionAction",
3202 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3203 "tools.shapes.spiral", "revolution", 3.0,
3204 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3205 0.01, 1024.0, 0.1, 1.0,
3206 labels, values, G_N_ELEMENTS(labels),
3207 sp_spl_tb_revolution_value_changed, 1, 2);
3208 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3209 }
3211 /* Expansion */
3212 {
3213 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3214 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3215 eact = create_adjustment_action( "SpiralExpansionAction",
3216 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3217 "tools.shapes.spiral", "expansion", 1.0,
3218 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3219 0.0, 1000.0, 0.01, 1.0,
3220 labels, values, G_N_ELEMENTS(labels),
3221 sp_spl_tb_expansion_value_changed);
3222 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3223 }
3225 /* T0 */
3226 {
3227 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3228 gdouble values[] = {0, 0.5, 0.9};
3229 eact = create_adjustment_action( "SpiralT0Action",
3230 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3231 "tools.shapes.spiral", "t0", 0.0,
3232 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3233 0.0, 0.999, 0.01, 1.0,
3234 labels, values, G_N_ELEMENTS(labels),
3235 sp_spl_tb_t0_value_changed);
3236 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3237 }
3239 /* Reset */
3240 {
3241 InkAction* inky = ink_action_new( "SpiralResetAction",
3242 _("Defaults"),
3243 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3244 GTK_STOCK_CLEAR,
3245 secondarySize );
3246 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3247 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3248 }
3251 sigc::connection *connection = new sigc::connection(
3252 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3253 );
3254 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3255 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3256 }
3258 //########################
3259 //## Pen/Pencil ##
3260 //########################
3262 static void sp_pc_spiro_spline_mode_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
3263 {
3264 prefs_set_int_attribute("tools.freehand", "spiro-spline-mode", ege_select_one_action_get_active(act));
3265 }
3267 static void sp_add_spiro_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3268 {
3269 // FIXME: No action is needed, we only want a simple label. But sp_toolbox_add_label() always
3270 // adds the label at the end of the toolbar, whence this workarund. How to avoid this?
3271 {
3272 EgeOutputAction* act = ege_output_action_new(
3273 tool_is_pencil ?
3274 "FreehandModeActionPencilTemp" :
3275 "FreehandModeActionPenTemp",
3276 _("<b>Mode:</b>"), "", 0 );
3277 ege_output_action_set_use_markup( act, TRUE );
3278 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3279 g_object_set_data( holder, "freehand_mode_action", act );
3280 }
3282 /* Freehand mode toggle buttons */
3283 {
3284 //gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
3285 //bool isSpiroMode = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
3286 guint spiroMode = prefs_get_int_attribute("tools.freehand", "spiro-spline-mode", 0);
3287 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3289 {
3290 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3292 GtkTreeIter iter;
3293 gtk_list_store_append( model, &iter );
3294 gtk_list_store_set( model, &iter,
3295 0, _("Bézier"),
3296 1, _("Regular Bézier mode"),
3297 2, "bezier_mode",
3298 -1 );
3300 gtk_list_store_append( model, &iter );
3301 gtk_list_store_set( model, &iter,
3302 0, _("Spiro"),
3303 1, _("Spiro splines mode"),
3304 2, "spiro_splines_mode",
3305 -1 );
3307 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3308 "FreehandModeActionPencil" :
3309 "FreehandModeActionPen",
3310 (""), (""), NULL, GTK_TREE_MODEL(model) );
3311 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3312 g_object_set_data( holder, "freehande_mode_action", act );
3314 ege_select_one_action_set_appearance( act, "full" );
3315 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3316 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3317 ege_select_one_action_set_icon_column( act, 2 );
3318 ege_select_one_action_set_icon_size( act, secondarySize );
3319 ege_select_one_action_set_tooltip_column( act, 1 );
3321 ege_select_one_action_set_active( act, spiroMode);
3322 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_pc_spiro_spline_mode_changed), holder);
3323 }
3324 }
3325 }
3327 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3328 {
3329 sp_add_spiro_toggle(mainActions, holder, false);
3330 }
3333 static void
3334 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3335 {
3336 GtkWidget *tbl = GTK_WIDGET(obj);
3338 GtkAdjustment *adj;
3340 // fixme: make settable
3341 gdouble tolerance = 4;
3343 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3344 gtk_adjustment_set_value(adj, tolerance);
3345 gtk_adjustment_value_changed(adj);
3347 spinbutton_defocus(GTK_OBJECT(tbl));
3348 }
3350 static void
3351 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3352 {
3354 // quit if run by the attr_changed listener
3355 if (g_object_get_data( tbl, "freeze" )) {
3356 return;
3357 }
3358 // in turn, prevent listener from responding
3359 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3360 prefs_set_double_attribute("tools.freehand.pencil",
3361 "tolerance", adj->value);
3362 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3364 }
3368 static void
3369 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node *repr,
3370 const gchar *key,
3371 const gchar *oldval,
3372 const gchar *newval,
3373 bool is_interactive,
3374 void * data)
3375 {
3376 GObject* tbl = G_OBJECT(data);
3377 if (g_object_get_data( tbl, "freeze" )) {
3378 return;
3379 }
3381 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3383 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3384 "tolerance");
3386 double v = prefs_get_double_attribute("tools.freehand.pencil",
3387 "tolerance", adj->value);
3388 gtk_adjustment_set_value(adj, v);
3389 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3391 }
3393 static Inkscape::XML::NodeEventVector pencil_node_events =
3394 {
3395 NULL,
3396 NULL,
3397 sp_pencil_tb_tolerance_value_changed_external,
3398 NULL,
3399 NULL,
3400 };
3403 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3404 {
3405 sp_add_spiro_toggle(mainActions, holder, true);
3407 EgeAdjustmentAction* eact = 0;
3409 /* Tolerance */
3410 {
3412 eact = create_adjustment_action( "PencilToleranceAction",
3413 _("Number of pixels allowed in interpolating"),
3414 _("Tolerance:"), _("Tolerance"),
3415 "tools.freehand.pencil", "tolerance",
3416 3.0,
3417 GTK_WIDGET(desktop->canvas), NULL,
3418 holder, TRUE, "altx-pencil",
3419 0.5, 100.0, 0.5, 1.0,
3420 NULL, NULL, 0,
3421 sp_pencil_tb_tolerance_value_changed,
3422 1, 2);
3423 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3425 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3426 "tools.freehand.pencil");
3427 repr->addListener(&pencil_node_events, G_OBJECT(holder));
3428 g_object_set_data(G_OBJECT(holder), "repr", repr);
3430 }
3431 /* Reset */
3432 {
3433 InkAction* inky = ink_action_new( "PencilResetAction",
3434 _("Defaults"),
3435 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3436 GTK_STOCK_CLEAR,
3437 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3438 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3439 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3440 }
3442 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3444 }
3447 //########################
3448 //## Tweak ##
3449 //########################
3451 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3452 {
3453 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3454 }
3456 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3457 {
3458 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3459 }
3461 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3462 {
3463 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3464 }
3466 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3467 {
3468 int mode = ege_select_one_action_get_active( act );
3469 prefs_set_int_attribute("tools.tweak", "mode", mode);
3471 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3472 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3473 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3474 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3475 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3476 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3477 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3478 if (doh) gtk_action_set_sensitive (doh, TRUE);
3479 if (dos) gtk_action_set_sensitive (dos, TRUE);
3480 if (dol) gtk_action_set_sensitive (dol, TRUE);
3481 if (doo) gtk_action_set_sensitive (doo, TRUE);
3482 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3483 if (fid) gtk_action_set_sensitive (fid, FALSE);
3484 } else {
3485 if (doh) gtk_action_set_sensitive (doh, FALSE);
3486 if (dos) gtk_action_set_sensitive (dos, FALSE);
3487 if (dol) gtk_action_set_sensitive (dol, FALSE);
3488 if (doo) gtk_action_set_sensitive (doo, FALSE);
3489 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3490 if (fid) gtk_action_set_sensitive (fid, TRUE);
3491 }
3492 }
3494 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3495 {
3496 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3497 }
3499 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3500 bool show = gtk_toggle_action_get_active( act );
3501 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3502 }
3503 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3504 bool show = gtk_toggle_action_get_active( act );
3505 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3506 }
3507 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3508 bool show = gtk_toggle_action_get_active( act );
3509 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3510 }
3511 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3512 bool show = gtk_toggle_action_get_active( act );
3513 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3514 }
3516 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3517 {
3518 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3520 {
3521 /* Width */
3522 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3523 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3524 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3525 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3526 "tools.tweak", "width", 15,
3527 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3528 1, 100, 1.0, 10.0,
3529 labels, values, G_N_ELEMENTS(labels),
3530 sp_tweak_width_value_changed, 0.01, 0, 100 );
3531 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3532 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3533 }
3536 {
3537 /* Force */
3538 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3539 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3540 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3541 _("Force"), _("Force:"), _("The force of the tweak action"),
3542 "tools.tweak", "force", 20,
3543 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3544 1, 100, 1.0, 10.0,
3545 labels, values, G_N_ELEMENTS(labels),
3546 sp_tweak_force_value_changed, 0.01, 0, 100 );
3547 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3548 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3549 }
3551 /* Mode */
3552 {
3553 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3555 GtkTreeIter iter;
3556 gtk_list_store_append( model, &iter );
3557 gtk_list_store_set( model, &iter,
3558 0, _("Push mode"),
3559 1, _("Push parts of paths in any direction"),
3560 2, "tweak_push_mode",
3561 -1 );
3563 gtk_list_store_append( model, &iter );
3564 gtk_list_store_set( model, &iter,
3565 0, _("Shrink mode"),
3566 1, _("Shrink (inset) parts of paths"),
3567 2, "tweak_shrink_mode",
3568 -1 );
3570 gtk_list_store_append( model, &iter );
3571 gtk_list_store_set( model, &iter,
3572 0, _("Grow mode"),
3573 1, _("Grow (outset) parts of paths"),
3574 2, "tweak_grow_mode",
3575 -1 );
3577 gtk_list_store_append( model, &iter );
3578 gtk_list_store_set( model, &iter,
3579 0, _("Attract mode"),
3580 1, _("Attract parts of paths towards cursor"),
3581 2, "tweak_attract_mode",
3582 -1 );
3584 gtk_list_store_append( model, &iter );
3585 gtk_list_store_set( model, &iter,
3586 0, _("Repel mode"),
3587 1, _("Repel parts of paths from cursor"),
3588 2, "tweak_repel_mode",
3589 -1 );
3591 gtk_list_store_append( model, &iter );
3592 gtk_list_store_set( model, &iter,
3593 0, _("Roughen mode"),
3594 1, _("Roughen parts of paths"),
3595 2, "tweak_roughen_mode",
3596 -1 );
3598 gtk_list_store_append( model, &iter );
3599 gtk_list_store_set( model, &iter,
3600 0, _("Color paint mode"),
3601 1, _("Paint the tool's color upon selected objects"),
3602 2, "tweak_colorpaint_mode",
3603 -1 );
3605 gtk_list_store_append( model, &iter );
3606 gtk_list_store_set( model, &iter,
3607 0, _("Color jitter mode"),
3608 1, _("Jitter the colors of selected objects"),
3609 2, "tweak_colorjitter_mode",
3610 -1 );
3612 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3613 g_object_set( act, "short_label", _("Mode:"), NULL );
3614 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3615 g_object_set_data( holder, "mode_action", act );
3617 ege_select_one_action_set_appearance( act, "full" );
3618 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3619 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3620 ege_select_one_action_set_icon_column( act, 2 );
3621 ege_select_one_action_set_icon_size( act, secondarySize );
3622 ege_select_one_action_set_tooltip_column( act, 1 );
3624 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3625 ege_select_one_action_set_active( act, mode );
3626 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3628 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3629 }
3631 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3633 {
3634 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3635 ege_output_action_set_use_markup( act, TRUE );
3636 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3637 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3638 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3639 g_object_set_data( holder, "tweak_channels_label", act);
3640 }
3642 {
3643 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3644 _("Hue"),
3645 _("In color mode, act on objects' hue"),
3646 NULL,
3647 Inkscape::ICON_SIZE_DECORATION );
3648 //TRANSLATORS: "H" here stands for hue
3649 g_object_set( act, "short_label", _("H"), NULL );
3650 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3651 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3652 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3653 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3654 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3655 g_object_set_data( holder, "tweak_doh", act);
3656 }
3657 {
3658 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3659 _("Saturation"),
3660 _("In color mode, act on objects' saturation"),
3661 NULL,
3662 Inkscape::ICON_SIZE_DECORATION );
3663 //TRANSLATORS: "S" here stands for Saturation
3664 g_object_set( act, "short_label", _("S"), NULL );
3665 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3666 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3667 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3668 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3669 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3670 g_object_set_data( holder, "tweak_dos", act );
3671 }
3672 {
3673 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3674 _("Lightness"),
3675 _("In color mode, act on objects' lightness"),
3676 NULL,
3677 Inkscape::ICON_SIZE_DECORATION );
3678 //TRANSLATORS: "L" here stands for Lightness
3679 g_object_set( act, "short_label", _("L"), NULL );
3680 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3681 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3682 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3683 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3684 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3685 g_object_set_data( holder, "tweak_dol", act );
3686 }
3687 {
3688 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3689 _("Opacity"),
3690 _("In color mode, act on objects' opacity"),
3691 NULL,
3692 Inkscape::ICON_SIZE_DECORATION );
3693 //TRANSLATORS: "O" here stands for Opacity
3694 g_object_set( act, "short_label", _("O"), NULL );
3695 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3696 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3697 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3698 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3699 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3700 g_object_set_data( holder, "tweak_doo", act );
3701 }
3703 { /* Fidelity */
3704 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3705 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3706 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3707 _("Fidelity"), _("Fidelity:"),
3708 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3709 "tools.tweak", "fidelity", 50,
3710 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3711 1, 100, 1.0, 10.0,
3712 labels, values, G_N_ELEMENTS(labels),
3713 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3714 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3715 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3716 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3717 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3718 g_object_set_data( holder, "tweak_fidelity", eact );
3719 }
3722 /* Use Pressure button */
3723 {
3724 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3725 _("Pressure"),
3726 _("Use the pressure of the input device to alter the force of tweak action"),
3727 "use_pressure",
3728 Inkscape::ICON_SIZE_DECORATION );
3729 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3730 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3731 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3732 }
3734 }
3737 //########################
3738 //## Calligraphy ##
3739 //########################
3740 static void update_presets_list(GObject *dataKludge ){
3741 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3742 if (sel) {
3743 ege_select_one_action_set_active(sel, 0 );
3744 }
3745 }
3747 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3748 {
3749 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3750 update_presets_list(tbl);
3751 }
3753 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3754 {
3755 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3756 update_presets_list(tbl);
3757 }
3759 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3760 {
3761 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3762 update_presets_list(tbl);
3763 }
3765 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3766 {
3767 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3768 update_presets_list(tbl);
3769 }
3771 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3772 {
3773 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3774 update_presets_list(tbl);
3775 }
3777 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3778 {
3779 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3780 update_presets_list(tbl);
3781 }
3783 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3784 {
3785 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3786 update_presets_list(tbl);
3787 }
3789 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3790 {
3791 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3792 update_presets_list(tbl);
3793 }
3795 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3796 {
3797 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3798 update_presets_list(tbl);
3799 }
3801 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
3802 {
3803 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3804 update_presets_list(tbl);
3805 }
3807 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
3808 {
3809 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3810 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3811 update_presets_list(tbl);
3812 if (calligraphy_angle )
3813 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3814 }
3817 #define PROFILE_FLOAT_SIZE 7
3818 #define PROFILE_INT_SIZE 4
3819 struct ProfileFloatElement {
3820 char const *name;
3821 double def;
3822 double min;
3823 double max;
3824 };
3825 struct ProfileIntElement {
3826 char const *name;
3827 int def;
3828 int min;
3829 int max;
3830 };
3834 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3835 {"mass",0.02, 0.0, 1.0},
3836 {"wiggle",0.0, 0.0, 1.0},
3837 {"angle",30.0, -90.0, 90.0},
3838 {"thinning",0.1, -1.0, 1.0},
3839 {"tremor",0.0, 0.0, 1.0},
3840 {"flatness",0.9, 0.0, 1.0},
3841 {"cap_rounding",0.0, 0.0, 5.0}
3842 };
3843 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3844 {"width",15, 1, 100},
3845 {"usepressure",1,0,1},
3846 {"tracebackground",0,0,1},
3847 {"usetilt",1,0,1},
3848 };
3852 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3853 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3854 if (! desktop) return;
3856 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3857 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3858 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3860 unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3861 gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3862 gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);
3864 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3865 ProfileFloatElement const &pe = f_profile[i];
3866 double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3867 prefs_set_double_attribute(pref_path,pe.name,v);
3868 }
3869 for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3870 ProfileIntElement const &pe = i_profile[i];
3871 int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3872 prefs_set_int_attribute(pref_path,pe.name,v);
3873 }
3874 prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3876 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3877 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3878 GtkTreeIter iter;
3879 gtk_list_store_append( model, &iter );
3880 gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3882 free(profile_id);
3883 free(pref_path);
3885 ege_select_one_action_set_active(selector, new_index);
3886 }
3889 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3891 gint preset_index = ege_select_one_action_get_active( act );
3892 gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3894 if ( profile_name) {
3895 g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3896 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3897 ProfileFloatElement const &pe = f_profile[i];
3898 double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3899 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3900 if ( adj ) {
3901 gtk_adjustment_set_value(adj, v);
3902 }
3903 }
3904 for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3905 ProfileIntElement const &pe = i_profile[i];
3906 int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3907 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3908 if ( toggle ) {
3909 gtk_toggle_action_set_active(toggle, v);
3910 } else printf("No toggle");
3911 }
3912 free(profile_name);
3913 g_object_set_data(dataKludge, "profile_selector",act); //restore selector visibility
3914 }
3916 }
3919 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3920 {
3921 {
3922 EgeAdjustmentAction* calligraphy_angle = 0;
3924 {
3925 /* Width */
3926 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3927 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3928 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3929 _("Pen Width"), _("Width:"),
3930 _("The width of the calligraphic pen (relative to the visible canvas area)"),
3931 "tools.calligraphic", "width", 15,
3932 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3933 1, 100, 1.0, 10.0,
3934 labels, values, G_N_ELEMENTS(labels),
3935 sp_ddc_width_value_changed, 0.01, 0, 100 );
3936 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3937 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3938 }
3940 {
3941 /* Thinning */
3942 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3943 gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3944 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3945 _("Stroke Thinning"), _("Thinning:"),
3946 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3947 "tools.calligraphic", "thinning", 0.1,
3948 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3949 -1.0, 1.0, 0.01, 0.1,
3950 labels, values, G_N_ELEMENTS(labels),
3951 sp_ddc_velthin_value_changed, 0.01, 2);
3952 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3953 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3954 }
3956 {
3957 /* Angle */
3958 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3959 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3960 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3961 _("Pen Angle"), _("Angle:"),
3962 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3963 "tools.calligraphic", "angle", 30,
3964 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3965 -90.0, 90.0, 1.0, 10.0,
3966 labels, values, G_N_ELEMENTS(labels),
3967 sp_ddc_angle_value_changed, 1, 0 );
3968 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3969 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3970 g_object_set_data( holder, "angle", eact );
3971 calligraphy_angle = eact;
3972 }
3974 {
3975 /* Fixation */
3976 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3977 gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3978 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3979 _("Fixation"), _("Fixation:"),
3980 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3981 "tools.calligraphic", "flatness", 0.9,
3982 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3983 0.0, 1.0, 0.01, 0.1,
3984 labels, values, G_N_ELEMENTS(labels),
3985 sp_ddc_flatness_value_changed, 0.01, 2 );
3986 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3987 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3988 }
3990 {
3991 /* Cap Rounding */
3992 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3993 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3994 // TRANSLATORS: "cap" means "end" (both start and finish) here
3995 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3996 _("Cap rounding"), _("Caps:"),
3997 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3998 "tools.calligraphic", "cap_rounding", 0.0,
3999 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4000 0.0, 5.0, 0.01, 0.1,
4001 labels, values, G_N_ELEMENTS(labels),
4002 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4003 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4004 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4005 }
4007 {
4008 /* Tremor */
4009 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4010 gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
4011 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4012 _("Stroke Tremor"), _("Tremor:"),
4013 _("Increase to make strokes rugged and trembling"),
4014 "tools.calligraphic", "tremor", 0.0,
4015 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4016 0.0, 1.0, 0.01, 0.1,
4017 labels, values, G_N_ELEMENTS(labels),
4018 sp_ddc_tremor_value_changed, 0.01, 2 );
4020 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4021 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4022 }
4024 {
4025 /* Wiggle */
4026 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4027 gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
4028 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4029 _("Pen Wiggle"), _("Wiggle:"),
4030 _("Increase to make the pen waver and wiggle"),
4031 "tools.calligraphic", "wiggle", 0.0,
4032 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4033 0.0, 1.0, 0.01, 0.1,
4034 labels, values, G_N_ELEMENTS(labels),
4035 sp_ddc_wiggle_value_changed, 0.01, 2 );
4036 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4037 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4038 }
4040 {
4041 /* Mass */
4042 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4043 gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
4044 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4045 _("Pen Mass"), _("Mass:"),
4046 _("Increase to make the pen drag behind, as if slowed by inertia"),
4047 "tools.calligraphic", "mass", 0.02,
4048 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4049 0.0, 1.0, 0.01, 0.1,
4050 labels, values, G_N_ELEMENTS(labels),
4051 sp_ddc_mass_value_changed, 0.01, 2 );
4052 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4053 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4054 }
4057 /* Trace Background button */
4058 {
4059 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4060 _("Trace Background"),
4061 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4062 "trace_background",
4063 Inkscape::ICON_SIZE_DECORATION );
4064 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4065 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4066 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4067 g_object_set_data( holder, "tracebackground", act );
4068 }
4070 /* Use Pressure button */
4071 {
4072 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4073 _("Pressure"),
4074 _("Use the pressure of the input device to alter the width of the pen"),
4075 "use_pressure",
4076 Inkscape::ICON_SIZE_DECORATION );
4077 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4078 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4079 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4080 g_object_set_data( holder, "usepressure", act );
4081 }
4083 /* Use Tilt button */
4084 {
4085 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4086 _("Tilt"),
4087 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4088 "use_tilt",
4089 Inkscape::ICON_SIZE_DECORATION );
4090 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4091 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4092 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4093 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4094 g_object_set_data( holder, "usetilt", act );
4095 }
4097 /*calligraphic profile */
4098 {
4099 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4100 gchar *pref_path;
4103 GtkTreeIter iter;
4104 gtk_list_store_append( model, &iter );
4105 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4107 //TODO: switch back to prefs API
4108 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4109 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4110 int ii=1;
4111 while (child_repr) {
4112 GtkTreeIter iter;
4113 char *preset_name = (char *) child_repr->attribute("name");
4114 gtk_list_store_append( model, &iter );
4115 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4116 child_repr = sp_repr_next(child_repr);
4117 }
4119 pref_path = NULL;
4120 EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model) );
4121 ege_select_one_action_set_appearance( act1, "compact" );
4122 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
4123 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4124 g_object_set_data( holder, "profile_selector", act1 );
4126 }
4128 /*Save or delete calligraphic profile */
4129 {
4130 GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
4131 _("Defaults"),
4132 _("Save current settings as new profile"),
4133 GTK_STOCK_SAVE );
4134 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
4137 gtk_action_group_add_action( mainActions, act );
4138 gtk_action_set_sensitive( act, TRUE );
4139 g_object_set_data( holder, "profile_save_delete", act );
4140 }
4141 }
4142 }
4145 //########################
4146 //## Circle / Arc ##
4147 //########################
4149 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4150 {
4151 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4152 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4154 if (v1 == 0 && v2 == 0) {
4155 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4156 gtk_action_set_sensitive( ocb, FALSE );
4157 gtk_action_set_sensitive( make_whole, FALSE );
4158 }
4159 } else {
4160 gtk_action_set_sensitive( ocb, TRUE );
4161 gtk_action_set_sensitive( make_whole, TRUE );
4162 }
4163 }
4165 static void
4166 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4167 {
4168 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4170 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4171 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4172 }
4174 // quit if run by the attr_changed listener
4175 if (g_object_get_data( tbl, "freeze" )) {
4176 return;
4177 }
4179 // in turn, prevent listener from responding
4180 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4182 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4184 bool modmade = false;
4185 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4186 items != NULL;
4187 items = items->next)
4188 {
4189 SPItem *item = SP_ITEM(items->data);
4191 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4193 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4194 SPArc *arc = SP_ARC(item);
4196 if (!strcmp(value_name, "start"))
4197 ge->start = (adj->value * M_PI)/ 180;
4198 else
4199 ge->end = (adj->value * M_PI)/ 180;
4201 sp_genericellipse_normalize(ge);
4202 ((SPObject *)arc)->updateRepr();
4203 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4205 modmade = true;
4206 }
4207 }
4209 g_free(namespaced_name);
4211 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4213 sp_arctb_sensitivize( tbl, adj->value, other->value );
4215 if (modmade) {
4216 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4217 _("Arc: Change start/end"));
4218 }
4220 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4221 }
4224 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4225 {
4226 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4227 }
4229 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4230 {
4231 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4232 }
4235 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4236 {
4237 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4238 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4239 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4240 prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4241 }
4243 // only take action if run by the attr_changed listener
4244 if (!g_object_get_data( tbl, "freeze" )) {
4245 // in turn, prevent listener from responding
4246 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4248 if ( eraserMode != 0 ) {
4249 } else {
4250 }
4251 // TODO finish implementation
4253 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4254 }
4255 }
4257 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4258 {
4259 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4260 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4261 if ( ege_select_one_action_get_active( act ) != 0 ) {
4262 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4263 } else {
4264 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4265 }
4266 }
4268 // quit if run by the attr_changed listener
4269 if (g_object_get_data( tbl, "freeze" )) {
4270 return;
4271 }
4273 // in turn, prevent listener from responding
4274 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4276 bool modmade = false;
4278 if ( ege_select_one_action_get_active(act) != 0 ) {
4279 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4280 items != NULL;
4281 items = items->next)
4282 {
4283 if (SP_IS_ARC((SPItem *) items->data)) {
4284 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4285 repr->setAttribute("sodipodi:open", "true");
4286 SP_OBJECT((SPItem *) items->data)->updateRepr();
4287 modmade = true;
4288 }
4289 }
4290 } else {
4291 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4292 items != NULL;
4293 items = items->next)
4294 {
4295 if (SP_IS_ARC((SPItem *) items->data)) {
4296 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4297 repr->setAttribute("sodipodi:open", NULL);
4298 SP_OBJECT((SPItem *) items->data)->updateRepr();
4299 modmade = true;
4300 }
4301 }
4302 }
4304 if (modmade) {
4305 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4306 _("Arc: Change open/closed"));
4307 }
4309 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4310 }
4312 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4313 {
4314 GtkAdjustment *adj;
4315 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4316 gtk_adjustment_set_value(adj, 0.0);
4317 gtk_adjustment_value_changed(adj);
4319 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4320 gtk_adjustment_set_value(adj, 0.0);
4321 gtk_adjustment_value_changed(adj);
4323 spinbutton_defocus( GTK_OBJECT(obj) );
4324 }
4326 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4327 gchar const */*old_value*/, gchar const */*new_value*/,
4328 bool /*is_interactive*/, gpointer data)
4329 {
4330 GObject *tbl = G_OBJECT(data);
4332 // quit if run by the _changed callbacks
4333 if (g_object_get_data( tbl, "freeze" )) {
4334 return;
4335 }
4337 // in turn, prevent callbacks from responding
4338 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4340 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4341 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4343 GtkAdjustment *adj1,*adj2;
4344 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4345 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4346 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4347 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4349 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4351 char const *openstr = NULL;
4352 openstr = repr->attribute("sodipodi:open");
4353 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4355 if (openstr) {
4356 ege_select_one_action_set_active( ocb, 1 );
4357 } else {
4358 ege_select_one_action_set_active( ocb, 0 );
4359 }
4361 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4362 }
4364 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4365 NULL, /* child_added */
4366 NULL, /* child_removed */
4367 arc_tb_event_attr_changed,
4368 NULL, /* content_changed */
4369 NULL /* order_changed */
4370 };
4373 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4374 {
4375 int n_selected = 0;
4376 Inkscape::XML::Node *repr = NULL;
4378 purge_repr_listener( tbl, tbl );
4380 for (GSList const *items = selection->itemList();
4381 items != NULL;
4382 items = items->next)
4383 {
4384 if (SP_IS_ARC((SPItem *) items->data)) {
4385 n_selected++;
4386 repr = SP_OBJECT_REPR((SPItem *) items->data);
4387 }
4388 }
4390 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4392 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4393 if (n_selected == 0) {
4394 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4395 } else if (n_selected == 1) {
4396 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4397 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4399 if (repr) {
4400 g_object_set_data( tbl, "repr", repr );
4401 Inkscape::GC::anchor(repr);
4402 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4403 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4404 }
4405 } else {
4406 // FIXME: implement averaging of all parameters for multiple selected
4407 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4408 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4409 sp_arctb_sensitivize( tbl, 1, 0 );
4410 }
4411 }
4414 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4415 {
4416 EgeAdjustmentAction* eact = 0;
4417 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4420 {
4421 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4422 ege_output_action_set_use_markup( act, TRUE );
4423 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4424 g_object_set_data( holder, "mode_action", act );
4425 }
4427 /* Start */
4428 {
4429 eact = create_adjustment_action( "ArcStartAction",
4430 _("Start"), _("Start:"),
4431 _("The angle (in degrees) from the horizontal to the arc's start point"),
4432 "tools.shapes.arc", "start", 0.0,
4433 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4434 -360.0, 360.0, 1.0, 10.0,
4435 0, 0, 0,
4436 sp_arctb_start_value_changed);
4437 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4438 }
4440 /* End */
4441 {
4442 eact = create_adjustment_action( "ArcEndAction",
4443 _("End"), _("End:"),
4444 _("The angle (in degrees) from the horizontal to the arc's end point"),
4445 "tools.shapes.arc", "end", 0.0,
4446 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4447 -360.0, 360.0, 1.0, 10.0,
4448 0, 0, 0,
4449 sp_arctb_end_value_changed);
4450 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4451 }
4453 /* Segments / Pie checkbox */
4454 {
4455 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4457 GtkTreeIter iter;
4458 gtk_list_store_append( model, &iter );
4459 gtk_list_store_set( model, &iter,
4460 0, _("Closed arc"),
4461 1, _("Switch to segment (closed shape with two radii)"),
4462 2, "circle_closed_arc",
4463 -1 );
4465 gtk_list_store_append( model, &iter );
4466 gtk_list_store_set( model, &iter,
4467 0, _("Open Arc"),
4468 1, _("Switch to arc (unclosed shape)"),
4469 2, "circle_open_arc",
4470 -1 );
4472 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4473 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4474 g_object_set_data( holder, "open_action", act );
4476 ege_select_one_action_set_appearance( act, "full" );
4477 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4478 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4479 ege_select_one_action_set_icon_column( act, 2 );
4480 ege_select_one_action_set_icon_size( act, secondarySize );
4481 ege_select_one_action_set_tooltip_column( act, 1 );
4483 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4484 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4485 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4486 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4487 }
4489 /* Make Whole */
4490 {
4491 InkAction* inky = ink_action_new( "ArcResetAction",
4492 _("Make whole"),
4493 _("Make the shape a whole ellipse, not arc or segment"),
4494 "reset_circle",
4495 secondarySize );
4496 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4497 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4498 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4499 g_object_set_data( holder, "make_whole", inky );
4500 }
4502 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4503 // sensitivize make whole and open checkbox
4504 {
4505 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4506 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4507 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4508 }
4511 sigc::connection *connection = new sigc::connection(
4512 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4513 );
4514 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4515 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4516 }
4521 // toggle button callbacks and updaters
4523 //########################
4524 //## Dropper ##
4525 //########################
4527 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4528 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4529 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4530 if ( set_action ) {
4531 if ( gtk_toggle_action_get_active( act ) ) {
4532 gtk_action_set_sensitive( set_action, TRUE );
4533 } else {
4534 gtk_action_set_sensitive( set_action, FALSE );
4535 }
4536 }
4538 spinbutton_defocus(GTK_OBJECT(tbl));
4539 }
4541 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4542 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4543 spinbutton_defocus(GTK_OBJECT(tbl));
4544 }
4547 /**
4548 * Dropper auxiliary toolbar construction and setup.
4549 *
4550 * TODO: Would like to add swatch of current color.
4551 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4552 * can drag and drop places. Will provide a nice mixing palette.
4553 */
4554 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4555 {
4556 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4558 {
4559 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4560 ege_output_action_set_use_markup( act, TRUE );
4561 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4562 }
4564 {
4565 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4566 _("Pick opacity"),
4567 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4568 NULL,
4569 Inkscape::ICON_SIZE_DECORATION );
4570 g_object_set( act, "short_label", _("Pick"), NULL );
4571 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4572 g_object_set_data( holder, "pick_action", act );
4573 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4574 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4575 }
4577 {
4578 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4579 _("Assign opacity"),
4580 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4581 NULL,
4582 Inkscape::ICON_SIZE_DECORATION );
4583 g_object_set( act, "short_label", _("Assign"), NULL );
4584 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4585 g_object_set_data( holder, "set_action", act );
4586 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4587 // make sure it's disabled if we're not picking alpha
4588 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4589 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4590 }
4591 }
4595 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4596 {
4597 {
4598 /* Width */
4599 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4600 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4601 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4602 _("Pen Width"), _("Width:"),
4603 _("The width of the eraser pen (relative to the visible canvas area)"),
4604 "tools.eraser", "width", 15,
4605 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4606 1, 100, 1.0, 10.0,
4607 labels, values, G_N_ELEMENTS(labels),
4608 sp_ddc_width_value_changed, 0.01, 0, 100 );
4609 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4610 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4611 }
4613 {
4614 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4616 GtkTreeIter iter;
4617 gtk_list_store_append( model, &iter );
4618 gtk_list_store_set( model, &iter,
4619 0, _("Delete"),
4620 1, _("Delete objects touched by the eraser"),
4621 2, "delete_object",
4622 -1 );
4624 gtk_list_store_append( model, &iter );
4625 gtk_list_store_set( model, &iter,
4626 0, _("Cut"),
4627 1, _("Cut out from objects"),
4628 2, "difference",
4629 -1 );
4631 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4632 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4633 g_object_set_data( holder, "eraser_mode_action", act );
4635 ege_select_one_action_set_appearance( act, "full" );
4636 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4637 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4638 ege_select_one_action_set_icon_column( act, 2 );
4639 ege_select_one_action_set_tooltip_column( act, 1 );
4641 gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4642 ege_select_one_action_set_active( act, eraserMode );
4643 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4644 }
4646 }
4648 //########################
4649 //## Text Toolbox ##
4650 //########################
4651 /*
4652 static void
4653 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4654 {
4655 //Call back for letter sizing spinbutton
4656 }
4658 static void
4659 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4660 {
4661 //Call back for line height spinbutton
4662 }
4664 static void
4665 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4666 {
4667 //Call back for horizontal kerning spinbutton
4668 }
4670 static void
4671 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4672 {
4673 //Call back for vertical kerning spinbutton
4674 }
4676 static void
4677 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4678 {
4679 //Call back for letter rotation spinbutton
4680 }*/
4682 namespace {
4684 bool popdown_visible = false;
4685 bool popdown_hasfocus = false;
4687 void
4688 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4689 {
4690 SPStyle *query =
4691 sp_style_new (SP_ACTIVE_DOCUMENT);
4693 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4695 int result_family =
4696 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4698 int result_style =
4699 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4701 int result_numbers =
4702 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4704 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4706 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4707 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4708 {
4709 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4711 if (repr)
4712 {
4713 sp_style_read_from_repr (query, repr);
4714 }
4715 else
4716 {
4717 return;
4718 }
4719 }
4721 if (query->text)
4722 {
4723 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4724 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4725 gtk_entry_set_text (GTK_ENTRY (entry), "");
4727 } else if (query->text->font_specification.value || query->text->font_family.value) {
4729 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4731 // Get the font that corresponds
4732 Glib::ustring familyName;
4734 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4735 if (font) {
4736 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4737 font->Unref();
4738 font = NULL;
4739 }
4741 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4743 Gtk::TreePath path;
4744 try {
4745 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4746 } catch (...) {
4747 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4748 return;
4749 }
4751 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4752 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4754 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4756 gtk_tree_selection_select_path (tselection, path.gobj());
4757 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4759 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4760 }
4762 //Size
4763 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4764 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4765 g_object_set_data (tbl, "size-block", gpointer(1));
4766 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4767 g_object_set_data (tbl, "size-block", gpointer(0));
4768 free (str);
4770 //Anchor
4771 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4772 {
4773 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4774 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4775 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4776 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4777 }
4778 else
4779 {
4780 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4781 {
4782 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4783 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4784 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4785 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4786 }
4787 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4788 {
4789 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4790 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4791 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4792 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4793 }
4794 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4795 {
4796 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4797 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4798 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4799 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4800 }
4801 }
4803 //Style
4804 {
4805 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4807 gboolean active = gtk_toggle_button_get_active (button);
4808 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4810 if (active != check)
4811 {
4812 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4813 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4814 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4815 }
4816 }
4818 {
4819 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4821 gboolean active = gtk_toggle_button_get_active (button);
4822 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4824 if (active != check)
4825 {
4826 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4827 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4828 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4829 }
4830 }
4832 //Orientation
4833 //locking both buttons, changing one affect all group (both)
4834 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4835 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4837 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4838 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4840 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4841 {
4842 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4843 }
4844 else
4845 {
4846 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4847 }
4848 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4849 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4850 }
4852 sp_style_unref(query);
4853 }
4855 void
4856 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4857 {
4858 sp_text_toolbox_selection_changed (selection, tbl);
4859 }
4861 void
4862 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4863 {
4864 sp_text_toolbox_selection_changed (NULL, tbl);
4865 }
4867 void
4868 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
4869 GObject *tbl)
4870 {
4871 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4872 GtkTreeModel *model = 0;
4873 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4874 GtkTreeIter iter;
4875 char *family = 0;
4877 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4878 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4880 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4881 return;
4882 }
4884 gtk_tree_model_get (model, &iter, 0, &family, -1);
4886 if (g_object_get_data (G_OBJECT (selection), "block"))
4887 {
4888 gtk_entry_set_text (GTK_ENTRY (entry), family);
4889 return;
4890 }
4892 gtk_entry_set_text (GTK_ENTRY (entry), family);
4894 SPStyle *query =
4895 sp_style_new (SP_ACTIVE_DOCUMENT);
4897 int result_fontspec =
4898 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4900 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4902 SPCSSAttr *css = sp_repr_css_attr_new ();
4905 // First try to get the font spec from the stored value
4906 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4908 if (fontSpec.empty()) {
4909 // Construct a new font specification if it does not yet exist
4910 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4911 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4912 fontFromStyle->Unref();
4913 }
4915 if (!fontSpec.empty()) {
4916 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4917 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4918 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4919 if (font) {
4920 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4922 // Set all the these just in case they were altered when finding the best
4923 // match for the new family and old style...
4925 gchar c[256];
4927 font->Family(c, 256);
4928 sp_repr_css_set_property (css, "font-family", c);
4930 font->Attribute( "weight", c, 256);
4931 sp_repr_css_set_property (css, "font-weight", c);
4933 font->Attribute("style", c, 256);
4934 sp_repr_css_set_property (css, "font-style", c);
4936 font->Attribute("stretch", c, 256);
4937 sp_repr_css_set_property (css, "font-stretch", c);
4939 font->Attribute("variant", c, 256);
4940 sp_repr_css_set_property (css, "font-variant", c);
4942 font->Unref();
4943 }
4944 }
4945 }
4947 // If querying returned nothing, set the default style of the tool (for new texts)
4948 if (result_fontspec == QUERY_STYLE_NOTHING)
4949 {
4950 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4951 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4952 }
4953 else
4954 {
4955 sp_desktop_set_style (desktop, css, true, true);
4956 }
4958 sp_style_unref(query);
4960 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4961 _("Text: Change font family"));
4962 sp_repr_css_attr_unref (css);
4963 free (family);
4964 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4966 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4967 }
4969 void
4970 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
4971 GObject *tbl)
4972 {
4973 const char *family = gtk_entry_get_text (entry);
4975 try {
4976 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4977 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4978 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4979 gtk_tree_selection_select_path (selection, path.gobj());
4980 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4981 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4982 } catch (...) {
4983 if (family && strlen (family))
4984 {
4985 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4986 }
4987 }
4988 }
4990 void
4991 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
4992 gpointer data)
4993 {
4994 if (g_object_get_data (G_OBJECT (button), "block")) return;
4995 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4996 int prop = GPOINTER_TO_INT(data);
4998 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4999 SPCSSAttr *css = sp_repr_css_attr_new ();
5001 switch (prop)
5002 {
5003 case 0:
5004 {
5005 sp_repr_css_set_property (css, "text-anchor", "start");
5006 sp_repr_css_set_property (css, "text-align", "start");
5007 break;
5008 }
5009 case 1:
5010 {
5011 sp_repr_css_set_property (css, "text-anchor", "middle");
5012 sp_repr_css_set_property (css, "text-align", "center");
5013 break;
5014 }
5016 case 2:
5017 {
5018 sp_repr_css_set_property (css, "text-anchor", "end");
5019 sp_repr_css_set_property (css, "text-align", "end");
5020 break;
5021 }
5023 case 3:
5024 {
5025 sp_repr_css_set_property (css, "text-anchor", "start");
5026 sp_repr_css_set_property (css, "text-align", "justify");
5027 break;
5028 }
5029 }
5031 SPStyle *query =
5032 sp_style_new (SP_ACTIVE_DOCUMENT);
5033 int result_numbers =
5034 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5036 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5037 if (result_numbers == QUERY_STYLE_NOTHING)
5038 {
5039 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5040 }
5042 sp_style_unref(query);
5044 sp_desktop_set_style (desktop, css, true, true);
5045 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5046 _("Text: Change alignment"));
5047 sp_repr_css_attr_unref (css);
5049 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5050 }
5052 void
5053 sp_text_toolbox_style_toggled (GtkToggleButton *button,
5054 gpointer data)
5055 {
5056 if (g_object_get_data (G_OBJECT (button), "block")) return;
5058 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5059 SPCSSAttr *css = sp_repr_css_attr_new ();
5060 int prop = GPOINTER_TO_INT(data);
5061 bool active = gtk_toggle_button_get_active (button);
5063 SPStyle *query =
5064 sp_style_new (SP_ACTIVE_DOCUMENT);
5066 int result_fontspec =
5067 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5069 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5070 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5071 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5073 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5074 Glib::ustring newFontSpec = "";
5076 if (fontSpec.empty()) {
5077 // Construct a new font specification if it does not yet exist
5078 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5079 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5080 fontFromStyle->Unref();
5081 }
5083 switch (prop)
5084 {
5085 case 0:
5086 {
5087 if (!fontSpec.empty()) {
5088 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5089 }
5090 if (fontSpec != newFontSpec) {
5091 // Don't even set the bold if the font didn't exist on the system
5092 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5093 }
5094 break;
5095 }
5097 case 1:
5098 {
5099 if (!fontSpec.empty()) {
5100 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5101 }
5102 if (fontSpec != newFontSpec) {
5103 // Don't even set the italic if the font didn't exist on the system
5104 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5105 }
5106 break;
5107 }
5108 }
5110 if (!newFontSpec.empty()) {
5111 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5112 }
5114 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5115 if (result_fontspec == QUERY_STYLE_NOTHING)
5116 {
5117 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5118 }
5120 sp_style_unref(query);
5122 sp_desktop_set_style (desktop, css, true, true);
5123 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5124 _("Text: Change font style"));
5125 sp_repr_css_attr_unref (css);
5127 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5128 }
5130 void
5131 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5132 gpointer data)
5133 {
5134 if (g_object_get_data (G_OBJECT (button), "block")) {
5135 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5136 return;
5137 }
5139 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5140 SPCSSAttr *css = sp_repr_css_attr_new ();
5141 int prop = GPOINTER_TO_INT(data);
5143 switch (prop)
5144 {
5145 case 0:
5146 {
5147 sp_repr_css_set_property (css, "writing-mode", "lr");
5148 break;
5149 }
5151 case 1:
5152 {
5153 sp_repr_css_set_property (css, "writing-mode", "tb");
5154 break;
5155 }
5156 }
5158 SPStyle *query =
5159 sp_style_new (SP_ACTIVE_DOCUMENT);
5160 int result_numbers =
5161 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5163 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5164 if (result_numbers == QUERY_STYLE_NOTHING)
5165 {
5166 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5167 }
5169 sp_desktop_set_style (desktop, css, true, true);
5170 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5171 _("Text: Change orientation"));
5172 sp_repr_css_attr_unref (css);
5174 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5175 }
5177 gboolean
5178 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5179 {
5180 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5181 if (!desktop) return FALSE;
5183 switch (get_group0_keyval (event)) {
5184 case GDK_Escape: // defocus
5185 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5186 sp_text_toolbox_selection_changed (NULL, tbl); // update
5187 return TRUE; // I consumed the event
5188 break;
5189 }
5190 return FALSE;
5191 }
5193 gboolean
5194 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5195 {
5196 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5197 if (!desktop) return FALSE;
5199 switch (get_group0_keyval (event)) {
5200 case GDK_KP_Enter:
5201 case GDK_Return:
5202 case GDK_Escape: // defocus
5203 gtk_widget_hide (w);
5204 popdown_visible = false;
5205 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5206 return TRUE; // I consumed the event
5207 break;
5208 case GDK_w:
5209 case GDK_W:
5210 if (event->state & GDK_CONTROL_MASK) {
5211 gtk_widget_hide (w);
5212 popdown_visible = false;
5213 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5214 return TRUE; // I consumed the event
5215 }
5216 break;
5217 }
5218 return FALSE;
5219 }
5222 void
5223 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5224 GObject *tbl)
5225 {
5226 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5228 if (g_object_get_data (tbl, "size-block")) return;
5230 // If this is not from selecting a size in the list (in which case get_active will give the
5231 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5232 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5233 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5234 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5235 return;
5237 gchar *endptr;
5238 gdouble value = -1;
5239 char *text = gtk_combo_box_get_active_text (cbox);
5240 if (text) {
5241 value = g_strtod (text, &endptr);
5242 if (endptr == text) // conversion failed, non-numeric input
5243 value = -1;
5244 free (text);
5245 }
5246 if (value <= 0) {
5247 return; // could not parse value
5248 }
5250 SPCSSAttr *css = sp_repr_css_attr_new ();
5251 Inkscape::CSSOStringStream osfs;
5252 osfs << value;
5253 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5255 SPStyle *query =
5256 sp_style_new (SP_ACTIVE_DOCUMENT);
5257 int result_numbers =
5258 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5260 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5261 if (result_numbers == QUERY_STYLE_NOTHING)
5262 {
5263 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5264 }
5266 sp_style_unref(query);
5268 sp_desktop_set_style (desktop, css, true, true);
5269 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5270 _("Text: Change font size"));
5271 sp_repr_css_attr_unref (css);
5273 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5274 }
5276 gboolean
5277 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5278 {
5279 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5280 if (!desktop) return FALSE;
5282 if (!g_object_get_data (tbl, "esc-pressed")) {
5283 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5284 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5285 sp_text_toolbox_size_changed (cbox, tbl);
5286 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5287 }
5288 return FALSE; // I consumed the event
5289 }
5292 gboolean
5293 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5294 {
5295 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5296 if (!desktop) return FALSE;
5298 switch (get_group0_keyval (event)) {
5299 case GDK_Escape: // defocus
5300 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5301 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5302 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5303 return TRUE; // I consumed the event
5304 break;
5305 case GDK_Return: // defocus
5306 case GDK_KP_Enter:
5307 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5308 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5309 sp_text_toolbox_size_changed (cbox, tbl);
5310 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5311 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5312 return TRUE; // I consumed the event
5313 break;
5314 }
5315 return FALSE;
5316 }
5318 void
5319 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
5320 GObject *tbl)
5321 {
5322 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5323 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5324 int x, y;
5326 if (!popdown_visible)
5327 {
5328 gdk_window_get_origin (widget->window, &x, &y);
5329 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5330 gtk_widget_show_all (popdown);
5331 //sp_transientize (popdown);
5333 gdk_pointer_grab (widget->window, TRUE,
5334 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5335 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5336 GDK_POINTER_MOTION_MASK),
5337 NULL, NULL, GDK_CURRENT_TIME);
5339 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5341 popdown_visible = true;
5342 }
5343 else
5344 {
5345 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5346 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5347 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5348 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5349 gtk_widget_hide (popdown);
5350 popdown_visible = false;
5351 }
5352 }
5354 gboolean
5355 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
5356 GdkEventFocus */*event*/,
5357 GObject */*tbl*/)
5358 {
5359 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5360 return FALSE;
5361 }
5363 gboolean
5364 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
5365 GdkEventFocus */*event*/,
5366 GObject */*tbl*/)
5367 {
5368 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5370 if (popdown_hasfocus) {
5371 gtk_widget_hide (popdown);
5372 popdown_hasfocus = false;
5373 popdown_visible = false;
5374 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5375 return TRUE;
5376 }
5377 return FALSE;
5378 }
5380 gboolean
5381 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
5382 GdkEventFocus */*event*/,
5383 GObject */*tbl*/)
5384 {
5385 popdown_hasfocus = true;
5386 return TRUE;
5387 }
5390 void
5391 cell_data_func (GtkTreeViewColumn */*column*/,
5392 GtkCellRenderer *cell,
5393 GtkTreeModel *tree_model,
5394 GtkTreeIter *iter,
5395 gpointer /*data*/)
5396 {
5397 char *family,
5398 *family_escaped,
5399 *sample_escaped;
5401 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5403 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5405 family_escaped = g_markup_escape_text (family, -1);
5406 sample_escaped = g_markup_escape_text (sample, -1);
5408 std::stringstream markup;
5409 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5410 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5412 free (family);
5413 free (family_escaped);
5414 free (sample_escaped);
5415 }
5417 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5418 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5419 if (completion) {
5420 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5421 g_object_unref (completion);
5422 }
5423 }
5425 GtkWidget*
5426 sp_text_toolbox_new (SPDesktop *desktop)
5427 {
5428 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5429 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5431 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5432 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5434 GtkTooltips *tt = gtk_tooltips_new();
5435 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5437 ////////////Family
5438 //Window
5439 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5440 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5442 //Entry
5443 GtkWidget *entry = gtk_entry_new ();
5444 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5445 GtkEntryCompletion *completion = gtk_entry_completion_new ();
5446 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5447 gtk_entry_completion_set_text_column (completion, 0);
5448 gtk_entry_completion_set_minimum_key_length (completion, 1);
5449 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5450 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5451 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5452 gtk_toolbar_append_widget( tbl, entry, "", "" );
5453 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5455 //Button
5456 GtkWidget *button = gtk_button_new ();
5457 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5458 gtk_toolbar_append_widget( tbl, button, "", "");
5460 //Popdown
5461 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
5462 GtkWidget *treeview = gtk_tree_view_new ();
5464 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5465 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
5466 gtk_tree_view_column_pack_start (column, cell, FALSE);
5467 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5468 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5469 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5471 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5472 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5473 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5475 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5477 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5478 gtk_container_add (GTK_CONTAINER (sw), treeview);
5480 gtk_container_add (GTK_CONTAINER (window), sw);
5481 gtk_widget_set_size_request (window, 300, 450);
5483 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5484 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5485 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5487 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5489 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5490 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5491 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5493 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5494 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5496 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5497 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5498 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5499 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5500 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5502 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5503 GtkWidget *box = gtk_event_box_new ();
5504 gtk_container_add (GTK_CONTAINER (box), image);
5505 gtk_toolbar_append_widget( tbl, box, "", "");
5506 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5507 GtkTooltips *tooltips = gtk_tooltips_new ();
5508 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5509 gtk_widget_hide (GTK_WIDGET (box));
5510 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5512 ////////////Size
5513 const char *sizes[] = {
5514 "4", "6", "8", "9", "10", "11", "12", "13", "14",
5515 "16", "18", "20", "22", "24", "28",
5516 "32", "36", "40", "48", "56", "64", "72", "144"
5517 };
5519 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5520 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5521 gtk_widget_set_size_request (cbox, 80, -1);
5522 gtk_toolbar_append_widget( tbl, cbox, "", "");
5523 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5524 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5525 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5526 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5528 ////////////Text anchor
5529 GtkWidget *group = gtk_radio_button_new (NULL);
5530 GtkWidget *row = gtk_hbox_new (FALSE, 4);
5531 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5533 // left
5534 GtkWidget *rbutton = group;
5535 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5536 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5537 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5539 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5540 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
5541 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5542 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5544 // center
5545 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5546 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5547 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5548 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5550 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5551 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
5552 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5553 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5555 // right
5556 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5557 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5558 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5559 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5561 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5562 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5563 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5564 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5566 // fill
5567 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5568 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5569 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5570 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5572 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5573 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
5574 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5575 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5577 gtk_toolbar_append_widget( tbl, row, "", "");
5579 //spacer
5580 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5582 ////////////Text style
5583 row = gtk_hbox_new (FALSE, 4);
5585 // bold
5586 rbutton = gtk_toggle_button_new ();
5587 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5588 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5589 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5590 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5592 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5593 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
5594 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5596 // italic
5597 rbutton = gtk_toggle_button_new ();
5598 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5599 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5600 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5601 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5603 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5604 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
5605 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5607 gtk_toolbar_append_widget( tbl, row, "", "");
5609 //spacer
5610 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5612 ////////////Text orientation
5613 group = gtk_radio_button_new (NULL);
5614 row = gtk_hbox_new (FALSE, 4);
5615 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5617 // horizontal
5618 rbutton = group;
5619 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5620 gtk_container_add (GTK_CONTAINER (rbutton),
5621 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5622 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5623 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5625 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5626 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5627 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5629 // vertical
5630 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5631 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5632 gtk_container_add (GTK_CONTAINER (rbutton),
5633 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5634 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5635 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5637 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5638 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
5639 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5640 gtk_toolbar_append_widget( tbl, row, "", "" );
5643 //watch selection
5644 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5646 sigc::connection *c_selection_changed =
5647 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5648 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5649 pool->add_connection ("selection-changed", c_selection_changed);
5651 sigc::connection *c_selection_modified =
5652 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5653 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5654 pool->add_connection ("selection-modified", c_selection_modified);
5656 sigc::connection *c_subselection_changed =
5657 new sigc::connection (desktop->connectToolSubselectionChanged
5658 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5659 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5661 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5664 gtk_widget_show_all( GTK_WIDGET(tbl) );
5666 return GTK_WIDGET(tbl);
5667 } // end of sp_text_toolbox_new()
5669 }//<unnamed> namespace
5672 //#########################
5673 //## Connector ##
5674 //#########################
5676 static void sp_connector_path_set_avoid(void)
5677 {
5678 cc_selection_set_avoid(true);
5679 }
5682 static void sp_connector_path_set_ignore(void)
5683 {
5684 cc_selection_set_avoid(false);
5685 }
5689 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5690 {
5691 // quit if run by the _changed callbacks
5692 if (g_object_get_data( tbl, "freeze" )) {
5693 return;
5694 }
5696 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5697 SPDocument *doc = sp_desktop_document(desktop);
5699 if (!sp_document_get_undo_sensitive(doc))
5700 {
5701 return;
5702 }
5704 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5706 if ( repr->attribute("inkscape:connector-spacing") ) {
5707 gdouble priorValue = gtk_adjustment_get_value(adj);
5708 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5709 if ( priorValue == gtk_adjustment_get_value(adj) ) {
5710 return;
5711 }
5712 } else if ( adj->value == defaultConnSpacing ) {
5713 return;
5714 }
5716 // in turn, prevent callbacks from responding
5717 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5719 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5720 SP_OBJECT(desktop->namedview)->updateRepr();
5722 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5723 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5724 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5725 NR::Matrix m = NR::identity();
5726 avoid_item_move(&m, item);
5727 }
5729 if (items) {
5730 g_slist_free(items);
5731 }
5733 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5734 _("Change connector spacing"));
5736 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5738 spinbutton_defocus(GTK_OBJECT(tbl));
5739 }
5741 static void sp_connector_graph_layout(void)
5742 {
5743 if (!SP_ACTIVE_DESKTOP) return;
5745 // hack for clones, see comment in align-and-distribute.cpp
5746 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5747 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5749 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5751 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5753 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5754 }
5756 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5757 {
5758 if ( gtk_toggle_action_get_active( act ) ) {
5759 prefs_set_string_attribute("tools.connector", "directedlayout",
5760 "true");
5761 } else {
5762 prefs_set_string_attribute("tools.connector", "directedlayout",
5763 "false");
5764 }
5765 }
5767 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5768 {
5769 if ( gtk_toggle_action_get_active( act ) ) {
5770 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5771 "true");
5772 } else {
5773 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5774 "false");
5775 }
5776 }
5779 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5780 {
5781 prefs_set_double_attribute("tools.connector", "length", adj->value);
5782 spinbutton_defocus(GTK_OBJECT(tbl));
5783 }
5785 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5786 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5787 bool /*is_interactive*/, gpointer data)
5788 {
5789 GtkWidget *tbl = GTK_WIDGET(data);
5791 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5792 return;
5793 }
5794 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5795 return;
5796 }
5798 GtkAdjustment *adj = (GtkAdjustment*)
5799 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5800 gdouble spacing = defaultConnSpacing;
5801 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5803 gtk_adjustment_set_value(adj, spacing);
5804 }
5807 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5808 NULL, /* child_added */
5809 NULL, /* child_removed */
5810 connector_tb_event_attr_changed,
5811 NULL, /* content_changed */
5812 NULL /* order_changed */
5813 };
5816 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5817 {
5818 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5820 {
5821 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5822 _("Avoid"),
5823 _("Make connectors avoid selected objects"),
5824 "connector_avoid",
5825 secondarySize );
5826 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5827 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5828 }
5830 {
5831 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5832 _("Ignore"),
5833 _("Make connectors ignore selected objects"),
5834 "connector_ignore",
5835 secondarySize );
5836 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5837 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5838 }
5840 EgeAdjustmentAction* eact = 0;
5842 // Spacing spinbox
5843 eact = create_adjustment_action( "ConnectorSpacingAction",
5844 _("Connector Spacing"), _("Spacing:"),
5845 _("The amount of space left around objects by auto-routing connectors"),
5846 "tools.connector", "spacing", defaultConnSpacing,
5847 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5848 0, 100, 1.0, 10.0,
5849 0, 0, 0,
5850 connector_spacing_changed, 1, 0 );
5851 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5853 // Graph (connector network) layout
5854 {
5855 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5856 _("Graph"),
5857 _("Nicely arrange selected connector network"),
5858 "graph_layout",
5859 secondarySize );
5860 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5861 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5862 }
5864 // Default connector length spinbox
5865 eact = create_adjustment_action( "ConnectorLengthAction",
5866 _("Connector Length"), _("Length:"),
5867 _("Ideal length for connectors when layout is applied"),
5868 "tools.connector", "length", 100,
5869 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5870 10, 1000, 10.0, 100.0,
5871 0, 0, 0,
5872 connector_length_changed, 1, 0 );
5873 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5876 // Directed edges toggle button
5877 {
5878 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5879 _("Downwards"),
5880 _("Make connectors with end-markers (arrows) point downwards"),
5881 "directed_graph",
5882 Inkscape::ICON_SIZE_DECORATION );
5883 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5885 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5886 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5887 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5889 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5890 }
5892 // Avoid overlaps toggle button
5893 {
5894 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5895 _("Remove overlaps"),
5896 _("Do not allow overlapping shapes"),
5897 "remove_overlaps",
5898 Inkscape::ICON_SIZE_DECORATION );
5899 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5901 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5902 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5903 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5905 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5906 }
5908 // Code to watch for changes to the connector-spacing attribute in
5909 // the XML.
5910 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5911 g_assert(repr != NULL);
5913 purge_repr_listener( holder, holder );
5915 if (repr) {
5916 g_object_set_data( holder, "repr", repr );
5917 Inkscape::GC::anchor(repr);
5918 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5919 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5920 }
5921 } // end of sp_connector_toolbox_prep()
5924 //#########################
5925 //## Paintbucket ##
5926 //#########################
5928 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5929 {
5930 gint channels = ege_select_one_action_get_active( act );
5931 flood_channels_set_channels( channels );
5932 }
5934 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5935 {
5936 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5937 }
5939 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5940 {
5941 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5942 }
5944 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5945 {
5946 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5947 SPUnit const *unit = tracker->getActiveUnit();
5949 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5951 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5952 }
5954 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5955 {
5956 // FIXME: make defaults settable via Inkscape Options
5957 struct KeyValue {
5958 char const *key;
5959 double value;
5960 } const key_values[] = {
5961 {"threshold", 15},
5962 {"offset", 0.0}
5963 };
5965 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5966 KeyValue const &kv = key_values[i];
5967 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5968 if ( adj ) {
5969 gtk_adjustment_set_value(adj, kv.value);
5970 }
5971 }
5973 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5974 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5975 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5976 ege_select_one_action_set_active( autogap_action, 0 );
5977 }
5979 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5980 {
5981 EgeAdjustmentAction* eact = 0;
5983 {
5984 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5986 GList* items = 0;
5987 gint count = 0;
5988 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5989 {
5990 GtkTreeIter iter;
5991 gtk_list_store_append( model, &iter );
5992 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5993 count++;
5994 }
5995 g_list_free( items );
5996 items = 0;
5997 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5998 g_object_set( act1, "short_label", _("Fill by:"), NULL );
5999 ege_select_one_action_set_appearance( act1, "compact" );
6000 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6001 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6002 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6003 g_object_set_data( holder, "channels_action", act1 );
6004 }
6006 // Spacing spinbox
6007 {
6008 eact = create_adjustment_action(
6009 "ThresholdAction",
6010 _("Fill Threshold"), _("Threshold:"),
6011 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6012 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6013 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
6014 0, 0, 0,
6015 paintbucket_threshold_changed, 1, 0 );
6017 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6018 }
6020 // Create the units menu.
6021 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6022 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6023 if (stored_unit)
6024 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6025 g_object_set_data( holder, "tracker", tracker );
6026 {
6027 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6028 gtk_action_group_add_action( mainActions, act );
6029 }
6031 // Offset spinbox
6032 {
6033 eact = create_adjustment_action(
6034 "OffsetAction",
6035 _("Grow/shrink by"), _("Grow/shrink by:"),
6036 _("The amount to grow (positive) or shrink (negative) the created fill path"),
6037 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6038 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6039 0, 0, 0,
6040 paintbucket_offset_changed, 1, 2);
6041 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6043 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6044 }
6046 /* Auto Gap */
6047 {
6048 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6050 GList* items = 0;
6051 gint count = 0;
6052 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6053 {
6054 GtkTreeIter iter;
6055 gtk_list_store_append( model, &iter );
6056 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6057 count++;
6058 }
6059 g_list_free( items );
6060 items = 0;
6061 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6062 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6063 ege_select_one_action_set_appearance( act2, "compact" );
6064 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6065 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6066 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6067 g_object_set_data( holder, "autogap_action", act2 );
6068 }
6070 /* Reset */
6071 {
6072 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6073 _("Defaults"),
6074 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6075 GTK_STOCK_CLEAR );
6076 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6077 gtk_action_group_add_action( mainActions, act );
6078 gtk_action_set_sensitive( act, TRUE );
6079 }
6081 }
6083 /*
6084 Local Variables:
6085 mode:c++
6086 c-file-style:"stroustrup"
6087 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6088 indent-tabs-mode:nil
6089 fill-column:99
6090 End:
6091 */
6092 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :