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 ege_adjustment_action_set_appearance( eact, "full" );
3987 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3988 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3989 }
3991 {
3992 /* Cap Rounding */
3993 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3994 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3995 // TRANSLATORS: "cap" means "end" (both start and finish) here
3996 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3997 _("Cap rounding"), _("Caps:"),
3998 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3999 "tools.calligraphic", "cap_rounding", 0.0,
4000 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4001 0.0, 5.0, 0.01, 0.1,
4002 labels, values, G_N_ELEMENTS(labels),
4003 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4004 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4005 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4006 }
4008 {
4009 /* Tremor */
4010 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4011 gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
4012 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4013 _("Stroke Tremor"), _("Tremor:"),
4014 _("Increase to make strokes rugged and trembling"),
4015 "tools.calligraphic", "tremor", 0.0,
4016 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4017 0.0, 1.0, 0.01, 0.1,
4018 labels, values, G_N_ELEMENTS(labels),
4019 sp_ddc_tremor_value_changed, 0.01, 2 );
4021 ege_adjustment_action_set_appearance( eact, "full" );
4022 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4023 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4024 }
4026 {
4027 /* Wiggle */
4028 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4029 gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
4030 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4031 _("Pen Wiggle"), _("Wiggle:"),
4032 _("Increase to make the pen waver and wiggle"),
4033 "tools.calligraphic", "wiggle", 0.0,
4034 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4035 0.0, 1.0, 0.01, 0.1,
4036 labels, values, G_N_ELEMENTS(labels),
4037 sp_ddc_wiggle_value_changed, 0.01, 2 );
4038 ege_adjustment_action_set_appearance( eact, "full" );
4039 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4040 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4041 }
4043 {
4044 /* Mass */
4045 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4046 gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
4047 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4048 _("Pen Mass"), _("Mass:"),
4049 _("Increase to make the pen drag behind, as if slowed by inertia"),
4050 "tools.calligraphic", "mass", 0.02,
4051 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4052 0.0, 1.0, 0.01, 0.1,
4053 labels, values, G_N_ELEMENTS(labels),
4054 sp_ddc_mass_value_changed, 0.01, 2 );
4055 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4056 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4057 }
4060 /* Trace Background button */
4061 {
4062 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4063 _("Trace Background"),
4064 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4065 "trace_background",
4066 Inkscape::ICON_SIZE_DECORATION );
4067 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4068 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4069 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4070 g_object_set_data( holder, "tracebackground", act );
4071 }
4073 /* Use Pressure button */
4074 {
4075 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4076 _("Pressure"),
4077 _("Use the pressure of the input device to alter the width of the pen"),
4078 "use_pressure",
4079 Inkscape::ICON_SIZE_DECORATION );
4080 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4081 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4082 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4083 g_object_set_data( holder, "usepressure", act );
4084 }
4086 /* Use Tilt button */
4087 {
4088 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4089 _("Tilt"),
4090 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4091 "use_tilt",
4092 Inkscape::ICON_SIZE_DECORATION );
4093 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4094 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4095 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4096 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4097 g_object_set_data( holder, "usetilt", act );
4098 }
4100 /*calligraphic profile */
4101 {
4102 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4103 gchar *pref_path;
4106 GtkTreeIter iter;
4107 gtk_list_store_append( model, &iter );
4108 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4110 //TODO: switch back to prefs API
4111 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4112 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4113 int ii=1;
4114 while (child_repr) {
4115 GtkTreeIter iter;
4116 char *preset_name = (char *) child_repr->attribute("name");
4117 gtk_list_store_append( model, &iter );
4118 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4119 child_repr = sp_repr_next(child_repr);
4120 }
4122 pref_path = NULL;
4123 EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model) );
4124 ege_select_one_action_set_appearance( act1, "compact" );
4125 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
4126 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4127 g_object_set_data( holder, "profile_selector", act1 );
4129 }
4131 /*Save or delete calligraphic profile */
4132 {
4133 GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
4134 _("Defaults"),
4135 _("Save current settings as new profile"),
4136 GTK_STOCK_SAVE );
4137 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
4140 gtk_action_group_add_action( mainActions, act );
4141 gtk_action_set_sensitive( act, TRUE );
4142 g_object_set_data( holder, "profile_save_delete", act );
4143 }
4144 }
4145 }
4148 //########################
4149 //## Circle / Arc ##
4150 //########################
4152 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4153 {
4154 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4155 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4157 if (v1 == 0 && v2 == 0) {
4158 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4159 gtk_action_set_sensitive( ocb, FALSE );
4160 gtk_action_set_sensitive( make_whole, FALSE );
4161 }
4162 } else {
4163 gtk_action_set_sensitive( ocb, TRUE );
4164 gtk_action_set_sensitive( make_whole, TRUE );
4165 }
4166 }
4168 static void
4169 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4170 {
4171 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4173 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4174 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4175 }
4177 // quit if run by the attr_changed listener
4178 if (g_object_get_data( tbl, "freeze" )) {
4179 return;
4180 }
4182 // in turn, prevent listener from responding
4183 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4185 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4187 bool modmade = false;
4188 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4189 items != NULL;
4190 items = items->next)
4191 {
4192 SPItem *item = SP_ITEM(items->data);
4194 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4196 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4197 SPArc *arc = SP_ARC(item);
4199 if (!strcmp(value_name, "start"))
4200 ge->start = (adj->value * M_PI)/ 180;
4201 else
4202 ge->end = (adj->value * M_PI)/ 180;
4204 sp_genericellipse_normalize(ge);
4205 ((SPObject *)arc)->updateRepr();
4206 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4208 modmade = true;
4209 }
4210 }
4212 g_free(namespaced_name);
4214 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4216 sp_arctb_sensitivize( tbl, adj->value, other->value );
4218 if (modmade) {
4219 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4220 _("Arc: Change start/end"));
4221 }
4223 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4224 }
4227 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4228 {
4229 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4230 }
4232 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4233 {
4234 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4235 }
4238 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4239 {
4240 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4241 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4242 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4243 prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4244 }
4246 // only take action if run by the attr_changed listener
4247 if (!g_object_get_data( tbl, "freeze" )) {
4248 // in turn, prevent listener from responding
4249 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4251 if ( eraserMode != 0 ) {
4252 } else {
4253 }
4254 // TODO finish implementation
4256 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4257 }
4258 }
4260 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4261 {
4262 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4263 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4264 if ( ege_select_one_action_get_active( act ) != 0 ) {
4265 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4266 } else {
4267 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4268 }
4269 }
4271 // quit if run by the attr_changed listener
4272 if (g_object_get_data( tbl, "freeze" )) {
4273 return;
4274 }
4276 // in turn, prevent listener from responding
4277 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4279 bool modmade = false;
4281 if ( ege_select_one_action_get_active(act) != 0 ) {
4282 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4283 items != NULL;
4284 items = items->next)
4285 {
4286 if (SP_IS_ARC((SPItem *) items->data)) {
4287 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4288 repr->setAttribute("sodipodi:open", "true");
4289 SP_OBJECT((SPItem *) items->data)->updateRepr();
4290 modmade = true;
4291 }
4292 }
4293 } else {
4294 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4295 items != NULL;
4296 items = items->next)
4297 {
4298 if (SP_IS_ARC((SPItem *) items->data)) {
4299 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4300 repr->setAttribute("sodipodi:open", NULL);
4301 SP_OBJECT((SPItem *) items->data)->updateRepr();
4302 modmade = true;
4303 }
4304 }
4305 }
4307 if (modmade) {
4308 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4309 _("Arc: Change open/closed"));
4310 }
4312 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4313 }
4315 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4316 {
4317 GtkAdjustment *adj;
4318 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4319 gtk_adjustment_set_value(adj, 0.0);
4320 gtk_adjustment_value_changed(adj);
4322 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4323 gtk_adjustment_set_value(adj, 0.0);
4324 gtk_adjustment_value_changed(adj);
4326 spinbutton_defocus( GTK_OBJECT(obj) );
4327 }
4329 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4330 gchar const */*old_value*/, gchar const */*new_value*/,
4331 bool /*is_interactive*/, gpointer data)
4332 {
4333 GObject *tbl = G_OBJECT(data);
4335 // quit if run by the _changed callbacks
4336 if (g_object_get_data( tbl, "freeze" )) {
4337 return;
4338 }
4340 // in turn, prevent callbacks from responding
4341 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4343 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4344 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4346 GtkAdjustment *adj1,*adj2;
4347 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4348 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4349 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4350 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4352 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4354 char const *openstr = NULL;
4355 openstr = repr->attribute("sodipodi:open");
4356 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4358 if (openstr) {
4359 ege_select_one_action_set_active( ocb, 1 );
4360 } else {
4361 ege_select_one_action_set_active( ocb, 0 );
4362 }
4364 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4365 }
4367 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4368 NULL, /* child_added */
4369 NULL, /* child_removed */
4370 arc_tb_event_attr_changed,
4371 NULL, /* content_changed */
4372 NULL /* order_changed */
4373 };
4376 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4377 {
4378 int n_selected = 0;
4379 Inkscape::XML::Node *repr = NULL;
4381 purge_repr_listener( tbl, tbl );
4383 for (GSList const *items = selection->itemList();
4384 items != NULL;
4385 items = items->next)
4386 {
4387 if (SP_IS_ARC((SPItem *) items->data)) {
4388 n_selected++;
4389 repr = SP_OBJECT_REPR((SPItem *) items->data);
4390 }
4391 }
4393 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4395 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4396 if (n_selected == 0) {
4397 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4398 } else if (n_selected == 1) {
4399 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4400 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4402 if (repr) {
4403 g_object_set_data( tbl, "repr", repr );
4404 Inkscape::GC::anchor(repr);
4405 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4406 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4407 }
4408 } else {
4409 // FIXME: implement averaging of all parameters for multiple selected
4410 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4411 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4412 sp_arctb_sensitivize( tbl, 1, 0 );
4413 }
4414 }
4417 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4418 {
4419 EgeAdjustmentAction* eact = 0;
4420 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4423 {
4424 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4425 ege_output_action_set_use_markup( act, TRUE );
4426 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4427 g_object_set_data( holder, "mode_action", act );
4428 }
4430 /* Start */
4431 {
4432 eact = create_adjustment_action( "ArcStartAction",
4433 _("Start"), _("Start:"),
4434 _("The angle (in degrees) from the horizontal to the arc's start point"),
4435 "tools.shapes.arc", "start", 0.0,
4436 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4437 -360.0, 360.0, 1.0, 10.0,
4438 0, 0, 0,
4439 sp_arctb_start_value_changed);
4440 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4441 }
4443 /* End */
4444 {
4445 eact = create_adjustment_action( "ArcEndAction",
4446 _("End"), _("End:"),
4447 _("The angle (in degrees) from the horizontal to the arc's end point"),
4448 "tools.shapes.arc", "end", 0.0,
4449 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4450 -360.0, 360.0, 1.0, 10.0,
4451 0, 0, 0,
4452 sp_arctb_end_value_changed);
4453 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4454 }
4456 /* Segments / Pie checkbox */
4457 {
4458 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4460 GtkTreeIter iter;
4461 gtk_list_store_append( model, &iter );
4462 gtk_list_store_set( model, &iter,
4463 0, _("Closed arc"),
4464 1, _("Switch to segment (closed shape with two radii)"),
4465 2, "circle_closed_arc",
4466 -1 );
4468 gtk_list_store_append( model, &iter );
4469 gtk_list_store_set( model, &iter,
4470 0, _("Open Arc"),
4471 1, _("Switch to arc (unclosed shape)"),
4472 2, "circle_open_arc",
4473 -1 );
4475 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4476 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4477 g_object_set_data( holder, "open_action", act );
4479 ege_select_one_action_set_appearance( act, "full" );
4480 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4481 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4482 ege_select_one_action_set_icon_column( act, 2 );
4483 ege_select_one_action_set_icon_size( act, secondarySize );
4484 ege_select_one_action_set_tooltip_column( act, 1 );
4486 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4487 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4488 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4489 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4490 }
4492 /* Make Whole */
4493 {
4494 InkAction* inky = ink_action_new( "ArcResetAction",
4495 _("Make whole"),
4496 _("Make the shape a whole ellipse, not arc or segment"),
4497 "reset_circle",
4498 secondarySize );
4499 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4500 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4501 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4502 g_object_set_data( holder, "make_whole", inky );
4503 }
4505 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4506 // sensitivize make whole and open checkbox
4507 {
4508 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4509 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4510 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4511 }
4514 sigc::connection *connection = new sigc::connection(
4515 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4516 );
4517 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4518 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4519 }
4524 // toggle button callbacks and updaters
4526 //########################
4527 //## Dropper ##
4528 //########################
4530 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4531 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4532 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4533 if ( set_action ) {
4534 if ( gtk_toggle_action_get_active( act ) ) {
4535 gtk_action_set_sensitive( set_action, TRUE );
4536 } else {
4537 gtk_action_set_sensitive( set_action, FALSE );
4538 }
4539 }
4541 spinbutton_defocus(GTK_OBJECT(tbl));
4542 }
4544 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4545 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4546 spinbutton_defocus(GTK_OBJECT(tbl));
4547 }
4550 /**
4551 * Dropper auxiliary toolbar construction and setup.
4552 *
4553 * TODO: Would like to add swatch of current color.
4554 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4555 * can drag and drop places. Will provide a nice mixing palette.
4556 */
4557 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4558 {
4559 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4561 {
4562 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4563 ege_output_action_set_use_markup( act, TRUE );
4564 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4565 }
4567 {
4568 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4569 _("Pick opacity"),
4570 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4571 NULL,
4572 Inkscape::ICON_SIZE_DECORATION );
4573 g_object_set( act, "short_label", _("Pick"), NULL );
4574 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4575 g_object_set_data( holder, "pick_action", act );
4576 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4577 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4578 }
4580 {
4581 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4582 _("Assign opacity"),
4583 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4584 NULL,
4585 Inkscape::ICON_SIZE_DECORATION );
4586 g_object_set( act, "short_label", _("Assign"), NULL );
4587 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4588 g_object_set_data( holder, "set_action", act );
4589 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4590 // make sure it's disabled if we're not picking alpha
4591 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4592 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4593 }
4594 }
4598 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4599 {
4600 {
4601 /* Width */
4602 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4603 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4604 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4605 _("Pen Width"), _("Width:"),
4606 _("The width of the eraser pen (relative to the visible canvas area)"),
4607 "tools.eraser", "width", 15,
4608 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4609 1, 100, 1.0, 10.0,
4610 labels, values, G_N_ELEMENTS(labels),
4611 sp_ddc_width_value_changed, 0.01, 0, 100 );
4612 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4613 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4614 }
4616 {
4617 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4619 GtkTreeIter iter;
4620 gtk_list_store_append( model, &iter );
4621 gtk_list_store_set( model, &iter,
4622 0, _("Delete"),
4623 1, _("Delete objects touched by the eraser"),
4624 2, "delete_object",
4625 -1 );
4627 gtk_list_store_append( model, &iter );
4628 gtk_list_store_set( model, &iter,
4629 0, _("Cut"),
4630 1, _("Cut out from objects"),
4631 2, "difference",
4632 -1 );
4634 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4635 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4636 g_object_set_data( holder, "eraser_mode_action", act );
4638 ege_select_one_action_set_appearance( act, "full" );
4639 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4640 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4641 ege_select_one_action_set_icon_column( act, 2 );
4642 ege_select_one_action_set_tooltip_column( act, 1 );
4644 gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4645 ege_select_one_action_set_active( act, eraserMode );
4646 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4647 }
4649 }
4651 //########################
4652 //## Text Toolbox ##
4653 //########################
4654 /*
4655 static void
4656 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4657 {
4658 //Call back for letter sizing spinbutton
4659 }
4661 static void
4662 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4663 {
4664 //Call back for line height spinbutton
4665 }
4667 static void
4668 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4669 {
4670 //Call back for horizontal kerning spinbutton
4671 }
4673 static void
4674 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4675 {
4676 //Call back for vertical kerning spinbutton
4677 }
4679 static void
4680 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4681 {
4682 //Call back for letter rotation spinbutton
4683 }*/
4685 namespace {
4687 bool popdown_visible = false;
4688 bool popdown_hasfocus = false;
4690 void
4691 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4692 {
4693 SPStyle *query =
4694 sp_style_new (SP_ACTIVE_DOCUMENT);
4696 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4698 int result_family =
4699 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4701 int result_style =
4702 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4704 int result_numbers =
4705 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4707 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4709 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4710 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4711 {
4712 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4714 if (repr)
4715 {
4716 sp_style_read_from_repr (query, repr);
4717 }
4718 else
4719 {
4720 return;
4721 }
4722 }
4724 if (query->text)
4725 {
4726 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4727 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4728 gtk_entry_set_text (GTK_ENTRY (entry), "");
4730 } else if (query->text->font_specification.value || query->text->font_family.value) {
4732 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4734 // Get the font that corresponds
4735 Glib::ustring familyName;
4737 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4738 if (font) {
4739 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4740 font->Unref();
4741 font = NULL;
4742 }
4744 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4746 Gtk::TreePath path;
4747 try {
4748 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4749 } catch (...) {
4750 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4751 return;
4752 }
4754 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4755 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4757 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4759 gtk_tree_selection_select_path (tselection, path.gobj());
4760 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4762 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4763 }
4765 //Size
4766 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4767 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4768 g_object_set_data (tbl, "size-block", gpointer(1));
4769 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4770 g_object_set_data (tbl, "size-block", gpointer(0));
4771 free (str);
4773 //Anchor
4774 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4775 {
4776 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4777 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4778 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4779 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4780 }
4781 else
4782 {
4783 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4784 {
4785 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4786 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4787 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4788 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4789 }
4790 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4791 {
4792 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4793 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4794 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4795 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4796 }
4797 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4798 {
4799 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4800 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4801 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4802 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4803 }
4804 }
4806 //Style
4807 {
4808 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4810 gboolean active = gtk_toggle_button_get_active (button);
4811 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4813 if (active != check)
4814 {
4815 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4816 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4817 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4818 }
4819 }
4821 {
4822 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4824 gboolean active = gtk_toggle_button_get_active (button);
4825 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4827 if (active != check)
4828 {
4829 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4830 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4831 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4832 }
4833 }
4835 //Orientation
4836 //locking both buttons, changing one affect all group (both)
4837 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4838 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4840 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4841 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4843 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4844 {
4845 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4846 }
4847 else
4848 {
4849 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4850 }
4851 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4852 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4853 }
4855 sp_style_unref(query);
4856 }
4858 void
4859 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4860 {
4861 sp_text_toolbox_selection_changed (selection, tbl);
4862 }
4864 void
4865 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4866 {
4867 sp_text_toolbox_selection_changed (NULL, tbl);
4868 }
4870 void
4871 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
4872 GObject *tbl)
4873 {
4874 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4875 GtkTreeModel *model = 0;
4876 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4877 GtkTreeIter iter;
4878 char *family = 0;
4880 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4881 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4883 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4884 return;
4885 }
4887 gtk_tree_model_get (model, &iter, 0, &family, -1);
4889 if (g_object_get_data (G_OBJECT (selection), "block"))
4890 {
4891 gtk_entry_set_text (GTK_ENTRY (entry), family);
4892 return;
4893 }
4895 gtk_entry_set_text (GTK_ENTRY (entry), family);
4897 SPStyle *query =
4898 sp_style_new (SP_ACTIVE_DOCUMENT);
4900 int result_fontspec =
4901 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4903 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4905 SPCSSAttr *css = sp_repr_css_attr_new ();
4908 // First try to get the font spec from the stored value
4909 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4911 if (fontSpec.empty()) {
4912 // Construct a new font specification if it does not yet exist
4913 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4914 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4915 fontFromStyle->Unref();
4916 }
4918 if (!fontSpec.empty()) {
4919 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4920 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4921 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4922 if (font) {
4923 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4925 // Set all the these just in case they were altered when finding the best
4926 // match for the new family and old style...
4928 gchar c[256];
4930 font->Family(c, 256);
4931 sp_repr_css_set_property (css, "font-family", c);
4933 font->Attribute( "weight", c, 256);
4934 sp_repr_css_set_property (css, "font-weight", c);
4936 font->Attribute("style", c, 256);
4937 sp_repr_css_set_property (css, "font-style", c);
4939 font->Attribute("stretch", c, 256);
4940 sp_repr_css_set_property (css, "font-stretch", c);
4942 font->Attribute("variant", c, 256);
4943 sp_repr_css_set_property (css, "font-variant", c);
4945 font->Unref();
4946 }
4947 }
4948 }
4950 // If querying returned nothing, set the default style of the tool (for new texts)
4951 if (result_fontspec == QUERY_STYLE_NOTHING)
4952 {
4953 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4954 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4955 }
4956 else
4957 {
4958 sp_desktop_set_style (desktop, css, true, true);
4959 }
4961 sp_style_unref(query);
4963 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4964 _("Text: Change font family"));
4965 sp_repr_css_attr_unref (css);
4966 free (family);
4967 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4969 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4970 }
4972 void
4973 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
4974 GObject *tbl)
4975 {
4976 const char *family = gtk_entry_get_text (entry);
4978 try {
4979 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4980 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4981 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4982 gtk_tree_selection_select_path (selection, path.gobj());
4983 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4984 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4985 } catch (...) {
4986 if (family && strlen (family))
4987 {
4988 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4989 }
4990 }
4991 }
4993 void
4994 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
4995 gpointer data)
4996 {
4997 if (g_object_get_data (G_OBJECT (button), "block")) return;
4998 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4999 int prop = GPOINTER_TO_INT(data);
5001 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5002 SPCSSAttr *css = sp_repr_css_attr_new ();
5004 switch (prop)
5005 {
5006 case 0:
5007 {
5008 sp_repr_css_set_property (css, "text-anchor", "start");
5009 sp_repr_css_set_property (css, "text-align", "start");
5010 break;
5011 }
5012 case 1:
5013 {
5014 sp_repr_css_set_property (css, "text-anchor", "middle");
5015 sp_repr_css_set_property (css, "text-align", "center");
5016 break;
5017 }
5019 case 2:
5020 {
5021 sp_repr_css_set_property (css, "text-anchor", "end");
5022 sp_repr_css_set_property (css, "text-align", "end");
5023 break;
5024 }
5026 case 3:
5027 {
5028 sp_repr_css_set_property (css, "text-anchor", "start");
5029 sp_repr_css_set_property (css, "text-align", "justify");
5030 break;
5031 }
5032 }
5034 SPStyle *query =
5035 sp_style_new (SP_ACTIVE_DOCUMENT);
5036 int result_numbers =
5037 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5039 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5040 if (result_numbers == QUERY_STYLE_NOTHING)
5041 {
5042 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5043 }
5045 sp_style_unref(query);
5047 sp_desktop_set_style (desktop, css, true, true);
5048 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5049 _("Text: Change alignment"));
5050 sp_repr_css_attr_unref (css);
5052 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5053 }
5055 void
5056 sp_text_toolbox_style_toggled (GtkToggleButton *button,
5057 gpointer data)
5058 {
5059 if (g_object_get_data (G_OBJECT (button), "block")) return;
5061 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5062 SPCSSAttr *css = sp_repr_css_attr_new ();
5063 int prop = GPOINTER_TO_INT(data);
5064 bool active = gtk_toggle_button_get_active (button);
5066 SPStyle *query =
5067 sp_style_new (SP_ACTIVE_DOCUMENT);
5069 int result_fontspec =
5070 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5072 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5073 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5074 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5076 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5077 Glib::ustring newFontSpec = "";
5079 if (fontSpec.empty()) {
5080 // Construct a new font specification if it does not yet exist
5081 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5082 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5083 fontFromStyle->Unref();
5084 }
5086 switch (prop)
5087 {
5088 case 0:
5089 {
5090 if (!fontSpec.empty()) {
5091 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5092 }
5093 if (fontSpec != newFontSpec) {
5094 // Don't even set the bold if the font didn't exist on the system
5095 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5096 }
5097 break;
5098 }
5100 case 1:
5101 {
5102 if (!fontSpec.empty()) {
5103 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5104 }
5105 if (fontSpec != newFontSpec) {
5106 // Don't even set the italic if the font didn't exist on the system
5107 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5108 }
5109 break;
5110 }
5111 }
5113 if (!newFontSpec.empty()) {
5114 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5115 }
5117 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5118 if (result_fontspec == QUERY_STYLE_NOTHING)
5119 {
5120 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5121 }
5123 sp_style_unref(query);
5125 sp_desktop_set_style (desktop, css, true, true);
5126 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5127 _("Text: Change font style"));
5128 sp_repr_css_attr_unref (css);
5130 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5131 }
5133 void
5134 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5135 gpointer data)
5136 {
5137 if (g_object_get_data (G_OBJECT (button), "block")) {
5138 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5139 return;
5140 }
5142 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5143 SPCSSAttr *css = sp_repr_css_attr_new ();
5144 int prop = GPOINTER_TO_INT(data);
5146 switch (prop)
5147 {
5148 case 0:
5149 {
5150 sp_repr_css_set_property (css, "writing-mode", "lr");
5151 break;
5152 }
5154 case 1:
5155 {
5156 sp_repr_css_set_property (css, "writing-mode", "tb");
5157 break;
5158 }
5159 }
5161 SPStyle *query =
5162 sp_style_new (SP_ACTIVE_DOCUMENT);
5163 int result_numbers =
5164 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5166 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5167 if (result_numbers == QUERY_STYLE_NOTHING)
5168 {
5169 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5170 }
5172 sp_desktop_set_style (desktop, css, true, true);
5173 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5174 _("Text: Change orientation"));
5175 sp_repr_css_attr_unref (css);
5177 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5178 }
5180 gboolean
5181 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5182 {
5183 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5184 if (!desktop) return FALSE;
5186 switch (get_group0_keyval (event)) {
5187 case GDK_Escape: // defocus
5188 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5189 sp_text_toolbox_selection_changed (NULL, tbl); // update
5190 return TRUE; // I consumed the event
5191 break;
5192 }
5193 return FALSE;
5194 }
5196 gboolean
5197 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5198 {
5199 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5200 if (!desktop) return FALSE;
5202 switch (get_group0_keyval (event)) {
5203 case GDK_KP_Enter:
5204 case GDK_Return:
5205 case GDK_Escape: // defocus
5206 gtk_widget_hide (w);
5207 popdown_visible = false;
5208 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5209 return TRUE; // I consumed the event
5210 break;
5211 case GDK_w:
5212 case GDK_W:
5213 if (event->state & GDK_CONTROL_MASK) {
5214 gtk_widget_hide (w);
5215 popdown_visible = false;
5216 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5217 return TRUE; // I consumed the event
5218 }
5219 break;
5220 }
5221 return FALSE;
5222 }
5225 void
5226 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5227 GObject *tbl)
5228 {
5229 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5231 if (g_object_get_data (tbl, "size-block")) return;
5233 // If this is not from selecting a size in the list (in which case get_active will give the
5234 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5235 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5236 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5237 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5238 return;
5240 gchar *endptr;
5241 gdouble value = -1;
5242 char *text = gtk_combo_box_get_active_text (cbox);
5243 if (text) {
5244 value = g_strtod (text, &endptr);
5245 if (endptr == text) // conversion failed, non-numeric input
5246 value = -1;
5247 free (text);
5248 }
5249 if (value <= 0) {
5250 return; // could not parse value
5251 }
5253 SPCSSAttr *css = sp_repr_css_attr_new ();
5254 Inkscape::CSSOStringStream osfs;
5255 osfs << value;
5256 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5258 SPStyle *query =
5259 sp_style_new (SP_ACTIVE_DOCUMENT);
5260 int result_numbers =
5261 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5263 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5264 if (result_numbers == QUERY_STYLE_NOTHING)
5265 {
5266 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5267 }
5269 sp_style_unref(query);
5271 sp_desktop_set_style (desktop, css, true, true);
5272 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5273 _("Text: Change font size"));
5274 sp_repr_css_attr_unref (css);
5276 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5277 }
5279 gboolean
5280 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5281 {
5282 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5283 if (!desktop) return FALSE;
5285 if (!g_object_get_data (tbl, "esc-pressed")) {
5286 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5287 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5288 sp_text_toolbox_size_changed (cbox, tbl);
5289 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5290 }
5291 return FALSE; // I consumed the event
5292 }
5295 gboolean
5296 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5297 {
5298 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5299 if (!desktop) return FALSE;
5301 switch (get_group0_keyval (event)) {
5302 case GDK_Escape: // defocus
5303 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5304 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5305 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5306 return TRUE; // I consumed the event
5307 break;
5308 case GDK_Return: // defocus
5309 case GDK_KP_Enter:
5310 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5311 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5312 sp_text_toolbox_size_changed (cbox, tbl);
5313 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5314 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5315 return TRUE; // I consumed the event
5316 break;
5317 }
5318 return FALSE;
5319 }
5321 void
5322 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
5323 GObject *tbl)
5324 {
5325 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5326 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5327 int x, y;
5329 if (!popdown_visible)
5330 {
5331 gdk_window_get_origin (widget->window, &x, &y);
5332 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5333 gtk_widget_show_all (popdown);
5334 //sp_transientize (popdown);
5336 gdk_pointer_grab (widget->window, TRUE,
5337 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5338 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5339 GDK_POINTER_MOTION_MASK),
5340 NULL, NULL, GDK_CURRENT_TIME);
5342 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5344 popdown_visible = true;
5345 }
5346 else
5347 {
5348 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5349 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5350 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5351 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5352 gtk_widget_hide (popdown);
5353 popdown_visible = false;
5354 }
5355 }
5357 gboolean
5358 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
5359 GdkEventFocus */*event*/,
5360 GObject */*tbl*/)
5361 {
5362 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5363 return FALSE;
5364 }
5366 gboolean
5367 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
5368 GdkEventFocus */*event*/,
5369 GObject */*tbl*/)
5370 {
5371 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5373 if (popdown_hasfocus) {
5374 gtk_widget_hide (popdown);
5375 popdown_hasfocus = false;
5376 popdown_visible = false;
5377 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5378 return TRUE;
5379 }
5380 return FALSE;
5381 }
5383 gboolean
5384 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
5385 GdkEventFocus */*event*/,
5386 GObject */*tbl*/)
5387 {
5388 popdown_hasfocus = true;
5389 return TRUE;
5390 }
5393 void
5394 cell_data_func (GtkTreeViewColumn */*column*/,
5395 GtkCellRenderer *cell,
5396 GtkTreeModel *tree_model,
5397 GtkTreeIter *iter,
5398 gpointer /*data*/)
5399 {
5400 char *family,
5401 *family_escaped,
5402 *sample_escaped;
5404 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5406 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5408 family_escaped = g_markup_escape_text (family, -1);
5409 sample_escaped = g_markup_escape_text (sample, -1);
5411 std::stringstream markup;
5412 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5413 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5415 free (family);
5416 free (family_escaped);
5417 free (sample_escaped);
5418 }
5420 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5421 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5422 if (completion) {
5423 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5424 g_object_unref (completion);
5425 }
5426 }
5428 GtkWidget*
5429 sp_text_toolbox_new (SPDesktop *desktop)
5430 {
5431 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5432 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5434 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5435 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5437 GtkTooltips *tt = gtk_tooltips_new();
5438 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5440 ////////////Family
5441 //Window
5442 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5443 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5445 //Entry
5446 GtkWidget *entry = gtk_entry_new ();
5447 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5448 GtkEntryCompletion *completion = gtk_entry_completion_new ();
5449 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5450 gtk_entry_completion_set_text_column (completion, 0);
5451 gtk_entry_completion_set_minimum_key_length (completion, 1);
5452 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5453 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5454 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5455 gtk_toolbar_append_widget( tbl, entry, "", "" );
5456 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5458 //Button
5459 GtkWidget *button = gtk_button_new ();
5460 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5461 gtk_toolbar_append_widget( tbl, button, "", "");
5463 //Popdown
5464 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
5465 GtkWidget *treeview = gtk_tree_view_new ();
5467 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5468 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
5469 gtk_tree_view_column_pack_start (column, cell, FALSE);
5470 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5471 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5472 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5474 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5475 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5476 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5478 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5480 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5481 gtk_container_add (GTK_CONTAINER (sw), treeview);
5483 gtk_container_add (GTK_CONTAINER (window), sw);
5484 gtk_widget_set_size_request (window, 300, 450);
5486 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5487 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5488 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5490 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5492 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5493 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5494 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5496 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5497 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5499 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5500 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5501 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5502 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5503 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5505 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5506 GtkWidget *box = gtk_event_box_new ();
5507 gtk_container_add (GTK_CONTAINER (box), image);
5508 gtk_toolbar_append_widget( tbl, box, "", "");
5509 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5510 GtkTooltips *tooltips = gtk_tooltips_new ();
5511 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5512 gtk_widget_hide (GTK_WIDGET (box));
5513 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5515 ////////////Size
5516 const char *sizes[] = {
5517 "4", "6", "8", "9", "10", "11", "12", "13", "14",
5518 "16", "18", "20", "22", "24", "28",
5519 "32", "36", "40", "48", "56", "64", "72", "144"
5520 };
5522 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5523 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5524 gtk_widget_set_size_request (cbox, 80, -1);
5525 gtk_toolbar_append_widget( tbl, cbox, "", "");
5526 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5527 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5528 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5529 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5531 ////////////Text anchor
5532 GtkWidget *group = gtk_radio_button_new (NULL);
5533 GtkWidget *row = gtk_hbox_new (FALSE, 4);
5534 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5536 // left
5537 GtkWidget *rbutton = group;
5538 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5539 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5540 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5542 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5543 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
5544 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5545 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5547 // center
5548 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5549 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5550 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5551 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5553 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5554 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
5555 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5556 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5558 // right
5559 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5560 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5561 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5562 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5564 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5565 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5566 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5567 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5569 // fill
5570 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5571 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5572 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5573 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5575 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5576 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
5577 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5578 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5580 gtk_toolbar_append_widget( tbl, row, "", "");
5582 //spacer
5583 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5585 ////////////Text style
5586 row = gtk_hbox_new (FALSE, 4);
5588 // bold
5589 rbutton = gtk_toggle_button_new ();
5590 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5591 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5592 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5593 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5595 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5596 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
5597 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5599 // italic
5600 rbutton = gtk_toggle_button_new ();
5601 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5602 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5603 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5604 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5606 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5607 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
5608 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5610 gtk_toolbar_append_widget( tbl, row, "", "");
5612 //spacer
5613 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5615 ////////////Text orientation
5616 group = gtk_radio_button_new (NULL);
5617 row = gtk_hbox_new (FALSE, 4);
5618 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5620 // horizontal
5621 rbutton = group;
5622 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5623 gtk_container_add (GTK_CONTAINER (rbutton),
5624 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5625 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5626 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5628 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5629 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5630 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5632 // vertical
5633 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5634 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5635 gtk_container_add (GTK_CONTAINER (rbutton),
5636 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5637 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5638 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5640 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5641 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
5642 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5643 gtk_toolbar_append_widget( tbl, row, "", "" );
5646 //watch selection
5647 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5649 sigc::connection *c_selection_changed =
5650 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5651 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5652 pool->add_connection ("selection-changed", c_selection_changed);
5654 sigc::connection *c_selection_modified =
5655 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5656 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5657 pool->add_connection ("selection-modified", c_selection_modified);
5659 sigc::connection *c_subselection_changed =
5660 new sigc::connection (desktop->connectToolSubselectionChanged
5661 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5662 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5664 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5667 gtk_widget_show_all( GTK_WIDGET(tbl) );
5669 return GTK_WIDGET(tbl);
5670 } // end of sp_text_toolbox_new()
5672 }//<unnamed> namespace
5675 //#########################
5676 //## Connector ##
5677 //#########################
5679 static void sp_connector_path_set_avoid(void)
5680 {
5681 cc_selection_set_avoid(true);
5682 }
5685 static void sp_connector_path_set_ignore(void)
5686 {
5687 cc_selection_set_avoid(false);
5688 }
5692 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5693 {
5694 // quit if run by the _changed callbacks
5695 if (g_object_get_data( tbl, "freeze" )) {
5696 return;
5697 }
5699 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5700 SPDocument *doc = sp_desktop_document(desktop);
5702 if (!sp_document_get_undo_sensitive(doc))
5703 {
5704 return;
5705 }
5707 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5709 if ( repr->attribute("inkscape:connector-spacing") ) {
5710 gdouble priorValue = gtk_adjustment_get_value(adj);
5711 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5712 if ( priorValue == gtk_adjustment_get_value(adj) ) {
5713 return;
5714 }
5715 } else if ( adj->value == defaultConnSpacing ) {
5716 return;
5717 }
5719 // in turn, prevent callbacks from responding
5720 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5722 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5723 SP_OBJECT(desktop->namedview)->updateRepr();
5725 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5726 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5727 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5728 NR::Matrix m = NR::identity();
5729 avoid_item_move(&m, item);
5730 }
5732 if (items) {
5733 g_slist_free(items);
5734 }
5736 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5737 _("Change connector spacing"));
5739 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5741 spinbutton_defocus(GTK_OBJECT(tbl));
5742 }
5744 static void sp_connector_graph_layout(void)
5745 {
5746 if (!SP_ACTIVE_DESKTOP) return;
5748 // hack for clones, see comment in align-and-distribute.cpp
5749 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5750 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5752 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5754 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5756 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5757 }
5759 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5760 {
5761 if ( gtk_toggle_action_get_active( act ) ) {
5762 prefs_set_string_attribute("tools.connector", "directedlayout",
5763 "true");
5764 } else {
5765 prefs_set_string_attribute("tools.connector", "directedlayout",
5766 "false");
5767 }
5768 }
5770 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5771 {
5772 if ( gtk_toggle_action_get_active( act ) ) {
5773 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5774 "true");
5775 } else {
5776 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5777 "false");
5778 }
5779 }
5782 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5783 {
5784 prefs_set_double_attribute("tools.connector", "length", adj->value);
5785 spinbutton_defocus(GTK_OBJECT(tbl));
5786 }
5788 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5789 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5790 bool /*is_interactive*/, gpointer data)
5791 {
5792 GtkWidget *tbl = GTK_WIDGET(data);
5794 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5795 return;
5796 }
5797 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5798 return;
5799 }
5801 GtkAdjustment *adj = (GtkAdjustment*)
5802 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5803 gdouble spacing = defaultConnSpacing;
5804 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5806 gtk_adjustment_set_value(adj, spacing);
5807 }
5810 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5811 NULL, /* child_added */
5812 NULL, /* child_removed */
5813 connector_tb_event_attr_changed,
5814 NULL, /* content_changed */
5815 NULL /* order_changed */
5816 };
5819 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5820 {
5821 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5823 {
5824 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5825 _("Avoid"),
5826 _("Make connectors avoid selected objects"),
5827 "connector_avoid",
5828 secondarySize );
5829 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5830 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5831 }
5833 {
5834 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5835 _("Ignore"),
5836 _("Make connectors ignore selected objects"),
5837 "connector_ignore",
5838 secondarySize );
5839 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5840 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5841 }
5843 EgeAdjustmentAction* eact = 0;
5845 // Spacing spinbox
5846 eact = create_adjustment_action( "ConnectorSpacingAction",
5847 _("Connector Spacing"), _("Spacing:"),
5848 _("The amount of space left around objects by auto-routing connectors"),
5849 "tools.connector", "spacing", defaultConnSpacing,
5850 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5851 0, 100, 1.0, 10.0,
5852 0, 0, 0,
5853 connector_spacing_changed, 1, 0 );
5854 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5856 // Graph (connector network) layout
5857 {
5858 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5859 _("Graph"),
5860 _("Nicely arrange selected connector network"),
5861 "graph_layout",
5862 secondarySize );
5863 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5864 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5865 }
5867 // Default connector length spinbox
5868 eact = create_adjustment_action( "ConnectorLengthAction",
5869 _("Connector Length"), _("Length:"),
5870 _("Ideal length for connectors when layout is applied"),
5871 "tools.connector", "length", 100,
5872 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5873 10, 1000, 10.0, 100.0,
5874 0, 0, 0,
5875 connector_length_changed, 1, 0 );
5876 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5879 // Directed edges toggle button
5880 {
5881 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5882 _("Downwards"),
5883 _("Make connectors with end-markers (arrows) point downwards"),
5884 "directed_graph",
5885 Inkscape::ICON_SIZE_DECORATION );
5886 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5888 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5889 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5890 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5892 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5893 }
5895 // Avoid overlaps toggle button
5896 {
5897 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5898 _("Remove overlaps"),
5899 _("Do not allow overlapping shapes"),
5900 "remove_overlaps",
5901 Inkscape::ICON_SIZE_DECORATION );
5902 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5904 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5905 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5906 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5908 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5909 }
5911 // Code to watch for changes to the connector-spacing attribute in
5912 // the XML.
5913 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5914 g_assert(repr != NULL);
5916 purge_repr_listener( holder, holder );
5918 if (repr) {
5919 g_object_set_data( holder, "repr", repr );
5920 Inkscape::GC::anchor(repr);
5921 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5922 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5923 }
5924 } // end of sp_connector_toolbox_prep()
5927 //#########################
5928 //## Paintbucket ##
5929 //#########################
5931 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5932 {
5933 gint channels = ege_select_one_action_get_active( act );
5934 flood_channels_set_channels( channels );
5935 }
5937 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5938 {
5939 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5940 }
5942 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5943 {
5944 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5945 }
5947 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5948 {
5949 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5950 SPUnit const *unit = tracker->getActiveUnit();
5952 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5954 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5955 }
5957 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5958 {
5959 // FIXME: make defaults settable via Inkscape Options
5960 struct KeyValue {
5961 char const *key;
5962 double value;
5963 } const key_values[] = {
5964 {"threshold", 15},
5965 {"offset", 0.0}
5966 };
5968 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5969 KeyValue const &kv = key_values[i];
5970 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5971 if ( adj ) {
5972 gtk_adjustment_set_value(adj, kv.value);
5973 }
5974 }
5976 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5977 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5978 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5979 ege_select_one_action_set_active( autogap_action, 0 );
5980 }
5982 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5983 {
5984 EgeAdjustmentAction* eact = 0;
5986 {
5987 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5989 GList* items = 0;
5990 gint count = 0;
5991 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5992 {
5993 GtkTreeIter iter;
5994 gtk_list_store_append( model, &iter );
5995 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5996 count++;
5997 }
5998 g_list_free( items );
5999 items = 0;
6000 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6001 g_object_set( act1, "short_label", _("Fill by:"), NULL );
6002 ege_select_one_action_set_appearance( act1, "compact" );
6003 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6004 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6005 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6006 g_object_set_data( holder, "channels_action", act1 );
6007 }
6009 // Spacing spinbox
6010 {
6011 eact = create_adjustment_action(
6012 "ThresholdAction",
6013 _("Fill Threshold"), _("Threshold:"),
6014 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6015 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6016 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
6017 0, 0, 0,
6018 paintbucket_threshold_changed, 1, 0 );
6020 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6021 }
6023 // Create the units menu.
6024 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6025 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6026 if (stored_unit)
6027 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6028 g_object_set_data( holder, "tracker", tracker );
6029 {
6030 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6031 gtk_action_group_add_action( mainActions, act );
6032 }
6034 // Offset spinbox
6035 {
6036 eact = create_adjustment_action(
6037 "OffsetAction",
6038 _("Grow/shrink by"), _("Grow/shrink by:"),
6039 _("The amount to grow (positive) or shrink (negative) the created fill path"),
6040 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6041 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6042 0, 0, 0,
6043 paintbucket_offset_changed, 1, 2);
6044 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6046 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6047 }
6049 /* Auto Gap */
6050 {
6051 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6053 GList* items = 0;
6054 gint count = 0;
6055 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6056 {
6057 GtkTreeIter iter;
6058 gtk_list_store_append( model, &iter );
6059 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6060 count++;
6061 }
6062 g_list_free( items );
6063 items = 0;
6064 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6065 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6066 ege_select_one_action_set_appearance( act2, "compact" );
6067 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6068 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6069 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6070 g_object_set_data( holder, "autogap_action", act2 );
6071 }
6073 /* Reset */
6074 {
6075 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6076 _("Defaults"),
6077 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6078 GTK_STOCK_CLEAR );
6079 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6080 gtk_action_group_add_action( mainActions, act );
6081 gtk_action_set_sensitive( act, TRUE );
6082 }
6084 }
6086 /*
6087 Local Variables:
6088 mode:c++
6089 c-file-style:"stroustrup"
6090 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6091 indent-tabs-mode:nil
6092 fill-column:99
6093 End:
6094 */
6095 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :