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, 0,
3420 NULL, NULL, 0,
3421 sp_pencil_tb_tolerance_value_changed,
3422 1, 2);
3423 ege_adjustment_action_set_appearance( eact, "full" );
3424 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3426 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3427 "tools.freehand.pencil");
3428 repr->addListener(&pencil_node_events, G_OBJECT(holder));
3429 g_object_set_data(G_OBJECT(holder), "repr", repr);
3431 }
3432 /* Reset */
3433 {
3434 InkAction* inky = ink_action_new( "PencilResetAction",
3435 _("Defaults"),
3436 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3437 GTK_STOCK_CLEAR,
3438 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3439 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3440 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3441 }
3443 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3445 }
3448 //########################
3449 //## Tweak ##
3450 //########################
3452 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3453 {
3454 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3455 }
3457 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3458 {
3459 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3460 }
3462 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3463 {
3464 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3465 }
3467 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3468 {
3469 int mode = ege_select_one_action_get_active( act );
3470 prefs_set_int_attribute("tools.tweak", "mode", mode);
3472 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3473 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3474 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3475 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3476 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3477 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3478 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3479 if (doh) gtk_action_set_sensitive (doh, TRUE);
3480 if (dos) gtk_action_set_sensitive (dos, TRUE);
3481 if (dol) gtk_action_set_sensitive (dol, TRUE);
3482 if (doo) gtk_action_set_sensitive (doo, TRUE);
3483 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3484 if (fid) gtk_action_set_sensitive (fid, FALSE);
3485 } else {
3486 if (doh) gtk_action_set_sensitive (doh, FALSE);
3487 if (dos) gtk_action_set_sensitive (dos, FALSE);
3488 if (dol) gtk_action_set_sensitive (dol, FALSE);
3489 if (doo) gtk_action_set_sensitive (doo, FALSE);
3490 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3491 if (fid) gtk_action_set_sensitive (fid, TRUE);
3492 }
3493 }
3495 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3496 {
3497 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3498 }
3500 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3501 bool show = gtk_toggle_action_get_active( act );
3502 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3503 }
3504 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3505 bool show = gtk_toggle_action_get_active( act );
3506 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3507 }
3508 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3509 bool show = gtk_toggle_action_get_active( act );
3510 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3511 }
3512 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3513 bool show = gtk_toggle_action_get_active( act );
3514 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3515 }
3517 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3518 {
3519 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3521 {
3522 /* Width */
3523 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3524 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3525 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3526 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3527 "tools.tweak", "width", 15,
3528 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3529 1, 100, 1.0, 0.0,
3530 labels, values, G_N_ELEMENTS(labels),
3531 sp_tweak_width_value_changed, 0.01, 0, 100 );
3532 ege_adjustment_action_set_appearance( eact, "full" );
3533 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3534 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3535 }
3538 {
3539 /* Force */
3540 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3541 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3542 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3543 _("Force"), _("Force:"), _("The force of the tweak action"),
3544 "tools.tweak", "force", 20,
3545 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3546 1, 100, 1.0, 0.0,
3547 labels, values, G_N_ELEMENTS(labels),
3548 sp_tweak_force_value_changed, 0.01, 0, 100 );
3549 ege_adjustment_action_set_appearance( eact, "full" );
3550 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3551 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3552 }
3554 /* Mode */
3555 {
3556 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3558 GtkTreeIter iter;
3559 gtk_list_store_append( model, &iter );
3560 gtk_list_store_set( model, &iter,
3561 0, _("Push mode"),
3562 1, _("Push parts of paths in any direction"),
3563 2, "tweak_push_mode",
3564 -1 );
3566 gtk_list_store_append( model, &iter );
3567 gtk_list_store_set( model, &iter,
3568 0, _("Shrink mode"),
3569 1, _("Shrink (inset) parts of paths"),
3570 2, "tweak_shrink_mode",
3571 -1 );
3573 gtk_list_store_append( model, &iter );
3574 gtk_list_store_set( model, &iter,
3575 0, _("Grow mode"),
3576 1, _("Grow (outset) parts of paths"),
3577 2, "tweak_grow_mode",
3578 -1 );
3580 gtk_list_store_append( model, &iter );
3581 gtk_list_store_set( model, &iter,
3582 0, _("Attract mode"),
3583 1, _("Attract parts of paths towards cursor"),
3584 2, "tweak_attract_mode",
3585 -1 );
3587 gtk_list_store_append( model, &iter );
3588 gtk_list_store_set( model, &iter,
3589 0, _("Repel mode"),
3590 1, _("Repel parts of paths from cursor"),
3591 2, "tweak_repel_mode",
3592 -1 );
3594 gtk_list_store_append( model, &iter );
3595 gtk_list_store_set( model, &iter,
3596 0, _("Roughen mode"),
3597 1, _("Roughen parts of paths"),
3598 2, "tweak_roughen_mode",
3599 -1 );
3601 gtk_list_store_append( model, &iter );
3602 gtk_list_store_set( model, &iter,
3603 0, _("Color paint mode"),
3604 1, _("Paint the tool's color upon selected objects"),
3605 2, "tweak_colorpaint_mode",
3606 -1 );
3608 gtk_list_store_append( model, &iter );
3609 gtk_list_store_set( model, &iter,
3610 0, _("Color jitter mode"),
3611 1, _("Jitter the colors of selected objects"),
3612 2, "tweak_colorjitter_mode",
3613 -1 );
3615 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3616 g_object_set( act, "short_label", _("Mode:"), NULL );
3617 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3618 g_object_set_data( holder, "mode_action", act );
3620 ege_select_one_action_set_appearance( act, "full" );
3621 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3622 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3623 ege_select_one_action_set_icon_column( act, 2 );
3624 ege_select_one_action_set_icon_size( act, secondarySize );
3625 ege_select_one_action_set_tooltip_column( act, 1 );
3627 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3628 ege_select_one_action_set_active( act, mode );
3629 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3631 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3632 }
3634 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3636 {
3637 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3638 ege_output_action_set_use_markup( act, TRUE );
3639 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3640 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3641 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3642 g_object_set_data( holder, "tweak_channels_label", act);
3643 }
3645 {
3646 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3647 _("Hue"),
3648 _("In color mode, act on objects' hue"),
3649 NULL,
3650 Inkscape::ICON_SIZE_DECORATION );
3651 //TRANSLATORS: "H" here stands for hue
3652 g_object_set( act, "short_label", _("H"), NULL );
3653 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3654 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3655 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3656 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3657 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3658 g_object_set_data( holder, "tweak_doh", act);
3659 }
3660 {
3661 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3662 _("Saturation"),
3663 _("In color mode, act on objects' saturation"),
3664 NULL,
3665 Inkscape::ICON_SIZE_DECORATION );
3666 //TRANSLATORS: "S" here stands for Saturation
3667 g_object_set( act, "short_label", _("S"), NULL );
3668 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3669 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3670 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3671 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3672 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3673 g_object_set_data( holder, "tweak_dos", act );
3674 }
3675 {
3676 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3677 _("Lightness"),
3678 _("In color mode, act on objects' lightness"),
3679 NULL,
3680 Inkscape::ICON_SIZE_DECORATION );
3681 //TRANSLATORS: "L" here stands for Lightness
3682 g_object_set( act, "short_label", _("L"), NULL );
3683 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3684 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3685 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3686 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3687 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3688 g_object_set_data( holder, "tweak_dol", act );
3689 }
3690 {
3691 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3692 _("Opacity"),
3693 _("In color mode, act on objects' opacity"),
3694 NULL,
3695 Inkscape::ICON_SIZE_DECORATION );
3696 //TRANSLATORS: "O" here stands for Opacity
3697 g_object_set( act, "short_label", _("O"), NULL );
3698 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3699 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3700 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3701 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3702 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3703 g_object_set_data( holder, "tweak_doo", act );
3704 }
3706 { /* Fidelity */
3707 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3708 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3709 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3710 _("Fidelity"), _("Fidelity:"),
3711 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3712 "tools.tweak", "fidelity", 50,
3713 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3714 1, 100, 1.0, 10.0,
3715 labels, values, G_N_ELEMENTS(labels),
3716 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3717 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3718 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3719 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3720 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3721 g_object_set_data( holder, "tweak_fidelity", eact );
3722 }
3725 /* Use Pressure button */
3726 {
3727 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3728 _("Pressure"),
3729 _("Use the pressure of the input device to alter the force of tweak action"),
3730 "use_pressure",
3731 Inkscape::ICON_SIZE_DECORATION );
3732 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3733 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3734 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3735 }
3737 }
3740 //########################
3741 //## Calligraphy ##
3742 //########################
3743 static void update_presets_list(GObject *dataKludge ){
3744 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3745 if (sel) {
3746 ege_select_one_action_set_active(sel, 0 );
3747 }
3748 }
3750 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3751 {
3752 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3753 update_presets_list(tbl);
3754 }
3756 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3757 {
3758 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3759 update_presets_list(tbl);
3760 }
3762 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3763 {
3764 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3765 update_presets_list(tbl);
3766 }
3768 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3769 {
3770 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3771 update_presets_list(tbl);
3772 }
3774 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3775 {
3776 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3777 update_presets_list(tbl);
3778 }
3780 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3781 {
3782 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3783 update_presets_list(tbl);
3784 }
3786 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3787 {
3788 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3789 update_presets_list(tbl);
3790 }
3792 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3793 {
3794 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3795 update_presets_list(tbl);
3796 }
3798 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3799 {
3800 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3801 update_presets_list(tbl);
3802 }
3804 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
3805 {
3806 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3807 update_presets_list(tbl);
3808 }
3810 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
3811 {
3812 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3813 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3814 update_presets_list(tbl);
3815 if (calligraphy_angle )
3816 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3817 }
3820 #define PROFILE_FLOAT_SIZE 7
3821 #define PROFILE_INT_SIZE 4
3822 struct ProfileFloatElement {
3823 char const *name;
3824 double def;
3825 double min;
3826 double max;
3827 };
3828 struct ProfileIntElement {
3829 char const *name;
3830 int def;
3831 int min;
3832 int max;
3833 };
3837 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3838 {"mass",0.02, 0.0, 1.0},
3839 {"wiggle",0.0, 0.0, 1.0},
3840 {"angle",30.0, -90.0, 90.0},
3841 {"thinning",0.1, -1.0, 1.0},
3842 {"tremor",0.0, 0.0, 1.0},
3843 {"flatness",0.9, 0.0, 1.0},
3844 {"cap_rounding",0.0, 0.0, 5.0}
3845 };
3846 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3847 {"width",15, 1, 100},
3848 {"usepressure",1,0,1},
3849 {"tracebackground",0,0,1},
3850 {"usetilt",1,0,1},
3851 };
3855 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3856 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3857 if (! desktop) return;
3859 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3860 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3861 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3863 unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3864 gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3865 gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);
3867 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3868 ProfileFloatElement const &pe = f_profile[i];
3869 double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3870 prefs_set_double_attribute(pref_path,pe.name,v);
3871 }
3872 for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3873 ProfileIntElement const &pe = i_profile[i];
3874 int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3875 prefs_set_int_attribute(pref_path,pe.name,v);
3876 }
3877 prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3879 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3880 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3881 GtkTreeIter iter;
3882 gtk_list_store_append( model, &iter );
3883 gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3885 free(profile_id);
3886 free(pref_path);
3888 ege_select_one_action_set_active(selector, new_index);
3889 }
3892 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3894 gint preset_index = ege_select_one_action_get_active( act );
3895 gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3897 if ( profile_name) {
3898 g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3899 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3900 ProfileFloatElement const &pe = f_profile[i];
3901 double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3902 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3903 if ( adj ) {
3904 gtk_adjustment_set_value(adj, v);
3905 }
3906 }
3907 for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3908 ProfileIntElement const &pe = i_profile[i];
3909 int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3910 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3911 if ( toggle ) {
3912 gtk_toggle_action_set_active(toggle, v);
3913 } else printf("No toggle");
3914 }
3915 free(profile_name);
3916 g_object_set_data(dataKludge, "profile_selector",act); //restore selector visibility
3917 }
3919 }
3922 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3923 {
3924 {
3925 EgeAdjustmentAction* calligraphy_angle = 0;
3927 {
3928 /* Width */
3929 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3930 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3931 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3932 _("Pen Width"), _("Width:"),
3933 _("The width of the calligraphic pen (relative to the visible canvas area)"),
3934 "tools.calligraphic", "width", 15,
3935 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3936 1, 100, 1.0, 0.0,
3937 labels, values, G_N_ELEMENTS(labels),
3938 sp_ddc_width_value_changed, 0.01, 0, 100 );
3939 ege_adjustment_action_set_appearance( eact, "full" );
3940 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3941 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3942 }
3944 {
3945 /* Thinning */
3946 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3947 gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3948 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3949 _("Stroke Thinning"), _("Thinning:"),
3950 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3951 "tools.calligraphic", "thinning", 0.1,
3952 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3953 -1.0, 1.0, 0.01, 0.1,
3954 labels, values, G_N_ELEMENTS(labels),
3955 sp_ddc_velthin_value_changed, 0.01, 2);
3956 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3957 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3958 }
3960 {
3961 /* Angle */
3962 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3963 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3964 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3965 _("Pen Angle"), _("Angle:"),
3966 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3967 "tools.calligraphic", "angle", 30,
3968 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3969 -90.0, 90.0, 1.0, 10.0,
3970 labels, values, G_N_ELEMENTS(labels),
3971 sp_ddc_angle_value_changed, 1, 0 );
3972 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3973 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3974 g_object_set_data( holder, "angle", eact );
3975 calligraphy_angle = eact;
3976 }
3978 {
3979 /* Fixation */
3980 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3981 gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3982 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3983 _("Fixation"), _("Fixation:"),
3984 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3985 "tools.calligraphic", "flatness", 0.9,
3986 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3987 0.0, 1.0, 0.01, 0.1,
3988 labels, values, G_N_ELEMENTS(labels),
3989 sp_ddc_flatness_value_changed, 0.01, 2 );
3990 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3991 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3992 }
3994 {
3995 /* Cap Rounding */
3996 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3997 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3998 // TRANSLATORS: "cap" means "end" (both start and finish) here
3999 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4000 _("Cap rounding"), _("Caps:"),
4001 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4002 "tools.calligraphic", "cap_rounding", 0.0,
4003 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4004 0.0, 5.0, 0.01, 0.1,
4005 labels, values, G_N_ELEMENTS(labels),
4006 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4007 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4008 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4009 }
4011 {
4012 /* Tremor */
4013 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4014 gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
4015 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4016 _("Stroke Tremor"), _("Tremor:"),
4017 _("Increase to make strokes rugged and trembling"),
4018 "tools.calligraphic", "tremor", 0.0,
4019 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4020 0.0, 1.0, 0.01, 0.0,
4021 labels, values, G_N_ELEMENTS(labels),
4022 sp_ddc_tremor_value_changed, 0.01, 2 );
4024 ege_adjustment_action_set_appearance( eact, "full" );
4025 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4026 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4027 }
4029 {
4030 /* Wiggle */
4031 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4032 gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
4033 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4034 _("Pen Wiggle"), _("Wiggle:"),
4035 _("Increase to make the pen waver and wiggle"),
4036 "tools.calligraphic", "wiggle", 0.0,
4037 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4038 0.0, 1.0, 0.01, 0.0,
4039 labels, values, G_N_ELEMENTS(labels),
4040 sp_ddc_wiggle_value_changed, 0.01, 2 );
4041 ege_adjustment_action_set_appearance( eact, "full" );
4042 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4043 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4044 }
4046 {
4047 /* Mass */
4048 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4049 gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
4050 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4051 _("Pen Mass"), _("Mass:"),
4052 _("Increase to make the pen drag behind, as if slowed by inertia"),
4053 "tools.calligraphic", "mass", 0.02,
4054 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4055 0.0, 1.0, 0.01, 0.0,
4056 labels, values, G_N_ELEMENTS(labels),
4057 sp_ddc_mass_value_changed, 0.01, 2 );
4058 ege_adjustment_action_set_appearance( eact, "full" );
4059 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4060 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4061 }
4064 /* Trace Background button */
4065 {
4066 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4067 _("Trace Background"),
4068 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4069 "trace_background",
4070 Inkscape::ICON_SIZE_DECORATION );
4071 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4072 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4073 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4074 g_object_set_data( holder, "tracebackground", act );
4075 }
4077 /* Use Pressure button */
4078 {
4079 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4080 _("Pressure"),
4081 _("Use the pressure of the input device to alter the width of the pen"),
4082 "use_pressure",
4083 Inkscape::ICON_SIZE_DECORATION );
4084 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4085 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4086 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4087 g_object_set_data( holder, "usepressure", act );
4088 }
4090 /* Use Tilt button */
4091 {
4092 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4093 _("Tilt"),
4094 _("Use the tilt of the input device to alter the angle of the pen's nib"),
4095 "use_tilt",
4096 Inkscape::ICON_SIZE_DECORATION );
4097 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4098 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4099 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4100 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4101 g_object_set_data( holder, "usetilt", act );
4102 }
4104 /*calligraphic profile */
4105 {
4106 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4107 gchar *pref_path;
4110 GtkTreeIter iter;
4111 gtk_list_store_append( model, &iter );
4112 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4114 //TODO: switch back to prefs API
4115 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4116 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4117 int ii=1;
4118 while (child_repr) {
4119 GtkTreeIter iter;
4120 char *preset_name = (char *) child_repr->attribute("name");
4121 gtk_list_store_append( model, &iter );
4122 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4123 child_repr = sp_repr_next(child_repr);
4124 }
4126 pref_path = NULL;
4127 EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model) );
4128 ege_select_one_action_set_appearance( act1, "compact" );
4129 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
4130 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4131 g_object_set_data( holder, "profile_selector", act1 );
4133 }
4135 /*Save or delete calligraphic profile */
4136 {
4137 GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
4138 _("Defaults"),
4139 _("Save current settings as new profile"),
4140 GTK_STOCK_SAVE );
4141 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
4144 gtk_action_group_add_action( mainActions, act );
4145 gtk_action_set_sensitive( act, TRUE );
4146 g_object_set_data( holder, "profile_save_delete", act );
4147 }
4148 }
4149 }
4152 //########################
4153 //## Circle / Arc ##
4154 //########################
4156 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4157 {
4158 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4159 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4161 if (v1 == 0 && v2 == 0) {
4162 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4163 gtk_action_set_sensitive( ocb, FALSE );
4164 gtk_action_set_sensitive( make_whole, FALSE );
4165 }
4166 } else {
4167 gtk_action_set_sensitive( ocb, TRUE );
4168 gtk_action_set_sensitive( make_whole, TRUE );
4169 }
4170 }
4172 static void
4173 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4174 {
4175 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4177 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4178 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4179 }
4181 // quit if run by the attr_changed listener
4182 if (g_object_get_data( tbl, "freeze" )) {
4183 return;
4184 }
4186 // in turn, prevent listener from responding
4187 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4189 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4191 bool modmade = false;
4192 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4193 items != NULL;
4194 items = items->next)
4195 {
4196 SPItem *item = SP_ITEM(items->data);
4198 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4200 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4201 SPArc *arc = SP_ARC(item);
4203 if (!strcmp(value_name, "start"))
4204 ge->start = (adj->value * M_PI)/ 180;
4205 else
4206 ge->end = (adj->value * M_PI)/ 180;
4208 sp_genericellipse_normalize(ge);
4209 ((SPObject *)arc)->updateRepr();
4210 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4212 modmade = true;
4213 }
4214 }
4216 g_free(namespaced_name);
4218 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4220 sp_arctb_sensitivize( tbl, adj->value, other->value );
4222 if (modmade) {
4223 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4224 _("Arc: Change start/end"));
4225 }
4227 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4228 }
4231 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4232 {
4233 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4234 }
4236 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4237 {
4238 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4239 }
4242 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4243 {
4244 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4245 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4246 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4247 prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4248 }
4250 // only take action if run by the attr_changed listener
4251 if (!g_object_get_data( tbl, "freeze" )) {
4252 // in turn, prevent listener from responding
4253 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4255 if ( eraserMode != 0 ) {
4256 } else {
4257 }
4258 // TODO finish implementation
4260 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4261 }
4262 }
4264 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4265 {
4266 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4267 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4268 if ( ege_select_one_action_get_active( act ) != 0 ) {
4269 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4270 } else {
4271 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4272 }
4273 }
4275 // quit if run by the attr_changed listener
4276 if (g_object_get_data( tbl, "freeze" )) {
4277 return;
4278 }
4280 // in turn, prevent listener from responding
4281 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4283 bool modmade = false;
4285 if ( ege_select_one_action_get_active(act) != 0 ) {
4286 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4287 items != NULL;
4288 items = items->next)
4289 {
4290 if (SP_IS_ARC((SPItem *) items->data)) {
4291 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4292 repr->setAttribute("sodipodi:open", "true");
4293 SP_OBJECT((SPItem *) items->data)->updateRepr();
4294 modmade = true;
4295 }
4296 }
4297 } else {
4298 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4299 items != NULL;
4300 items = items->next)
4301 {
4302 if (SP_IS_ARC((SPItem *) items->data)) {
4303 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4304 repr->setAttribute("sodipodi:open", NULL);
4305 SP_OBJECT((SPItem *) items->data)->updateRepr();
4306 modmade = true;
4307 }
4308 }
4309 }
4311 if (modmade) {
4312 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4313 _("Arc: Change open/closed"));
4314 }
4316 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4317 }
4319 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4320 {
4321 GtkAdjustment *adj;
4322 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4323 gtk_adjustment_set_value(adj, 0.0);
4324 gtk_adjustment_value_changed(adj);
4326 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4327 gtk_adjustment_set_value(adj, 0.0);
4328 gtk_adjustment_value_changed(adj);
4330 spinbutton_defocus( GTK_OBJECT(obj) );
4331 }
4333 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4334 gchar const */*old_value*/, gchar const */*new_value*/,
4335 bool /*is_interactive*/, gpointer data)
4336 {
4337 GObject *tbl = G_OBJECT(data);
4339 // quit if run by the _changed callbacks
4340 if (g_object_get_data( tbl, "freeze" )) {
4341 return;
4342 }
4344 // in turn, prevent callbacks from responding
4345 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4347 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4348 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4350 GtkAdjustment *adj1,*adj2;
4351 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4352 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4353 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4354 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4356 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4358 char const *openstr = NULL;
4359 openstr = repr->attribute("sodipodi:open");
4360 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4362 if (openstr) {
4363 ege_select_one_action_set_active( ocb, 1 );
4364 } else {
4365 ege_select_one_action_set_active( ocb, 0 );
4366 }
4368 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4369 }
4371 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4372 NULL, /* child_added */
4373 NULL, /* child_removed */
4374 arc_tb_event_attr_changed,
4375 NULL, /* content_changed */
4376 NULL /* order_changed */
4377 };
4380 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4381 {
4382 int n_selected = 0;
4383 Inkscape::XML::Node *repr = NULL;
4385 purge_repr_listener( tbl, tbl );
4387 for (GSList const *items = selection->itemList();
4388 items != NULL;
4389 items = items->next)
4390 {
4391 if (SP_IS_ARC((SPItem *) items->data)) {
4392 n_selected++;
4393 repr = SP_OBJECT_REPR((SPItem *) items->data);
4394 }
4395 }
4397 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4399 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4400 if (n_selected == 0) {
4401 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4402 } else if (n_selected == 1) {
4403 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4404 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4406 if (repr) {
4407 g_object_set_data( tbl, "repr", repr );
4408 Inkscape::GC::anchor(repr);
4409 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4410 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4411 }
4412 } else {
4413 // FIXME: implement averaging of all parameters for multiple selected
4414 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4415 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4416 sp_arctb_sensitivize( tbl, 1, 0 );
4417 }
4418 }
4421 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4422 {
4423 EgeAdjustmentAction* eact = 0;
4424 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4427 {
4428 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4429 ege_output_action_set_use_markup( act, TRUE );
4430 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4431 g_object_set_data( holder, "mode_action", act );
4432 }
4434 /* Start */
4435 {
4436 eact = create_adjustment_action( "ArcStartAction",
4437 _("Start"), _("Start:"),
4438 _("The angle (in degrees) from the horizontal to the arc's start point"),
4439 "tools.shapes.arc", "start", 0.0,
4440 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4441 -360.0, 360.0, 1.0, 10.0,
4442 0, 0, 0,
4443 sp_arctb_start_value_changed);
4444 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4445 }
4447 /* End */
4448 {
4449 eact = create_adjustment_action( "ArcEndAction",
4450 _("End"), _("End:"),
4451 _("The angle (in degrees) from the horizontal to the arc's end point"),
4452 "tools.shapes.arc", "end", 0.0,
4453 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4454 -360.0, 360.0, 1.0, 10.0,
4455 0, 0, 0,
4456 sp_arctb_end_value_changed);
4457 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4458 }
4460 /* Segments / Pie checkbox */
4461 {
4462 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4464 GtkTreeIter iter;
4465 gtk_list_store_append( model, &iter );
4466 gtk_list_store_set( model, &iter,
4467 0, _("Closed arc"),
4468 1, _("Switch to segment (closed shape with two radii)"),
4469 2, "circle_closed_arc",
4470 -1 );
4472 gtk_list_store_append( model, &iter );
4473 gtk_list_store_set( model, &iter,
4474 0, _("Open Arc"),
4475 1, _("Switch to arc (unclosed shape)"),
4476 2, "circle_open_arc",
4477 -1 );
4479 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4480 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4481 g_object_set_data( holder, "open_action", act );
4483 ege_select_one_action_set_appearance( act, "full" );
4484 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4485 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4486 ege_select_one_action_set_icon_column( act, 2 );
4487 ege_select_one_action_set_icon_size( act, secondarySize );
4488 ege_select_one_action_set_tooltip_column( act, 1 );
4490 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4491 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4492 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4493 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4494 }
4496 /* Make Whole */
4497 {
4498 InkAction* inky = ink_action_new( "ArcResetAction",
4499 _("Make whole"),
4500 _("Make the shape a whole ellipse, not arc or segment"),
4501 "reset_circle",
4502 secondarySize );
4503 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4504 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4505 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4506 g_object_set_data( holder, "make_whole", inky );
4507 }
4509 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4510 // sensitivize make whole and open checkbox
4511 {
4512 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4513 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4514 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4515 }
4518 sigc::connection *connection = new sigc::connection(
4519 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4520 );
4521 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4522 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4523 }
4528 // toggle button callbacks and updaters
4530 //########################
4531 //## Dropper ##
4532 //########################
4534 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4535 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4536 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4537 if ( set_action ) {
4538 if ( gtk_toggle_action_get_active( act ) ) {
4539 gtk_action_set_sensitive( set_action, TRUE );
4540 } else {
4541 gtk_action_set_sensitive( set_action, FALSE );
4542 }
4543 }
4545 spinbutton_defocus(GTK_OBJECT(tbl));
4546 }
4548 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4549 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4550 spinbutton_defocus(GTK_OBJECT(tbl));
4551 }
4554 /**
4555 * Dropper auxiliary toolbar construction and setup.
4556 *
4557 * TODO: Would like to add swatch of current color.
4558 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4559 * can drag and drop places. Will provide a nice mixing palette.
4560 */
4561 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4562 {
4563 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4565 {
4566 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4567 ege_output_action_set_use_markup( act, TRUE );
4568 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4569 }
4571 {
4572 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4573 _("Pick opacity"),
4574 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4575 NULL,
4576 Inkscape::ICON_SIZE_DECORATION );
4577 g_object_set( act, "short_label", _("Pick"), NULL );
4578 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4579 g_object_set_data( holder, "pick_action", act );
4580 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4581 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4582 }
4584 {
4585 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4586 _("Assign opacity"),
4587 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4588 NULL,
4589 Inkscape::ICON_SIZE_DECORATION );
4590 g_object_set( act, "short_label", _("Assign"), NULL );
4591 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4592 g_object_set_data( holder, "set_action", act );
4593 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4594 // make sure it's disabled if we're not picking alpha
4595 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4596 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4597 }
4598 }
4602 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4603 {
4604 {
4605 /* Width */
4606 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4607 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4608 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4609 _("Pen Width"), _("Width:"),
4610 _("The width of the eraser pen (relative to the visible canvas area)"),
4611 "tools.eraser", "width", 15,
4612 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4613 1, 100, 1.0, 0.0,
4614 labels, values, G_N_ELEMENTS(labels),
4615 sp_ddc_width_value_changed, 0.01, 0, 100 );
4616 ege_adjustment_action_set_appearance( eact, "full" );
4617 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4618 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4619 }
4621 {
4622 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4624 GtkTreeIter iter;
4625 gtk_list_store_append( model, &iter );
4626 gtk_list_store_set( model, &iter,
4627 0, _("Delete"),
4628 1, _("Delete objects touched by the eraser"),
4629 2, "delete_object",
4630 -1 );
4632 gtk_list_store_append( model, &iter );
4633 gtk_list_store_set( model, &iter,
4634 0, _("Cut"),
4635 1, _("Cut out from objects"),
4636 2, "difference",
4637 -1 );
4639 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4640 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4641 g_object_set_data( holder, "eraser_mode_action", act );
4643 ege_select_one_action_set_appearance( act, "full" );
4644 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4645 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4646 ege_select_one_action_set_icon_column( act, 2 );
4647 ege_select_one_action_set_tooltip_column( act, 1 );
4649 gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4650 ege_select_one_action_set_active( act, eraserMode );
4651 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4652 }
4654 }
4656 //########################
4657 //## Text Toolbox ##
4658 //########################
4659 /*
4660 static void
4661 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4662 {
4663 //Call back for letter sizing spinbutton
4664 }
4666 static void
4667 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4668 {
4669 //Call back for line height spinbutton
4670 }
4672 static void
4673 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4674 {
4675 //Call back for horizontal kerning spinbutton
4676 }
4678 static void
4679 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4680 {
4681 //Call back for vertical kerning spinbutton
4682 }
4684 static void
4685 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4686 {
4687 //Call back for letter rotation spinbutton
4688 }*/
4690 namespace {
4692 bool popdown_visible = false;
4693 bool popdown_hasfocus = false;
4695 void
4696 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4697 {
4698 SPStyle *query =
4699 sp_style_new (SP_ACTIVE_DOCUMENT);
4701 // int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4703 int result_family =
4704 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4706 int result_style =
4707 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4709 int result_numbers =
4710 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4712 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4714 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4715 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4716 {
4717 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4719 if (repr)
4720 {
4721 sp_style_read_from_repr (query, repr);
4722 }
4723 else
4724 {
4725 return;
4726 }
4727 }
4729 if (query->text)
4730 {
4731 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4732 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4733 gtk_entry_set_text (GTK_ENTRY (entry), "");
4735 } else if (query->text->font_specification.value || query->text->font_family.value) {
4737 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4739 // Get the font that corresponds
4740 Glib::ustring familyName;
4742 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4743 if (font) {
4744 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4745 font->Unref();
4746 font = NULL;
4747 }
4749 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4751 Gtk::TreePath path;
4752 try {
4753 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4754 } catch (...) {
4755 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4756 return;
4757 }
4759 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4760 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4762 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4764 gtk_tree_selection_select_path (tselection, path.gobj());
4765 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4767 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4768 }
4770 //Size
4771 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4772 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4773 g_object_set_data (tbl, "size-block", gpointer(1));
4774 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4775 g_object_set_data (tbl, "size-block", gpointer(0));
4776 free (str);
4778 //Anchor
4779 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4780 {
4781 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4782 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4783 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4784 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4785 }
4786 else
4787 {
4788 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4789 {
4790 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4791 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4792 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4793 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4794 }
4795 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4796 {
4797 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4798 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4799 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4800 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4801 }
4802 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4803 {
4804 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4805 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4806 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4807 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4808 }
4809 }
4811 //Style
4812 {
4813 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4815 gboolean active = gtk_toggle_button_get_active (button);
4816 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4818 if (active != check)
4819 {
4820 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4821 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4822 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4823 }
4824 }
4826 {
4827 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4829 gboolean active = gtk_toggle_button_get_active (button);
4830 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4832 if (active != check)
4833 {
4834 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4835 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4836 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4837 }
4838 }
4840 //Orientation
4841 //locking both buttons, changing one affect all group (both)
4842 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4843 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4845 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4846 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4848 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4849 {
4850 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4851 }
4852 else
4853 {
4854 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4855 }
4856 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4857 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4858 }
4860 sp_style_unref(query);
4861 }
4863 void
4864 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4865 {
4866 sp_text_toolbox_selection_changed (selection, tbl);
4867 }
4869 void
4870 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4871 {
4872 sp_text_toolbox_selection_changed (NULL, tbl);
4873 }
4875 void
4876 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
4877 GObject *tbl)
4878 {
4879 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4880 GtkTreeModel *model = 0;
4881 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4882 GtkTreeIter iter;
4883 char *family = 0;
4885 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4886 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4888 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4889 return;
4890 }
4892 gtk_tree_model_get (model, &iter, 0, &family, -1);
4894 if (g_object_get_data (G_OBJECT (selection), "block"))
4895 {
4896 gtk_entry_set_text (GTK_ENTRY (entry), family);
4897 return;
4898 }
4900 gtk_entry_set_text (GTK_ENTRY (entry), family);
4902 SPStyle *query =
4903 sp_style_new (SP_ACTIVE_DOCUMENT);
4905 int result_fontspec =
4906 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4908 //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4910 SPCSSAttr *css = sp_repr_css_attr_new ();
4913 // First try to get the font spec from the stored value
4914 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4916 if (fontSpec.empty()) {
4917 // Construct a new font specification if it does not yet exist
4918 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4919 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4920 fontFromStyle->Unref();
4921 }
4923 if (!fontSpec.empty()) {
4924 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4925 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4926 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4927 if (font) {
4928 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4930 // Set all the these just in case they were altered when finding the best
4931 // match for the new family and old style...
4933 gchar c[256];
4935 font->Family(c, 256);
4936 sp_repr_css_set_property (css, "font-family", c);
4938 font->Attribute( "weight", c, 256);
4939 sp_repr_css_set_property (css, "font-weight", c);
4941 font->Attribute("style", c, 256);
4942 sp_repr_css_set_property (css, "font-style", c);
4944 font->Attribute("stretch", c, 256);
4945 sp_repr_css_set_property (css, "font-stretch", c);
4947 font->Attribute("variant", c, 256);
4948 sp_repr_css_set_property (css, "font-variant", c);
4950 font->Unref();
4951 }
4952 }
4953 }
4955 // If querying returned nothing, set the default style of the tool (for new texts)
4956 if (result_fontspec == QUERY_STYLE_NOTHING)
4957 {
4958 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4959 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4960 }
4961 else
4962 {
4963 sp_desktop_set_style (desktop, css, true, true);
4964 }
4966 sp_style_unref(query);
4968 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4969 _("Text: Change font family"));
4970 sp_repr_css_attr_unref (css);
4971 free (family);
4972 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4974 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4975 }
4977 void
4978 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
4979 GObject *tbl)
4980 {
4981 const char *family = gtk_entry_get_text (entry);
4983 try {
4984 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4985 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4986 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4987 gtk_tree_selection_select_path (selection, path.gobj());
4988 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4989 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4990 } catch (...) {
4991 if (family && strlen (family))
4992 {
4993 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4994 }
4995 }
4996 }
4998 void
4999 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
5000 gpointer data)
5001 {
5002 if (g_object_get_data (G_OBJECT (button), "block")) return;
5003 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5004 int prop = GPOINTER_TO_INT(data);
5006 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5007 SPCSSAttr *css = sp_repr_css_attr_new ();
5009 switch (prop)
5010 {
5011 case 0:
5012 {
5013 sp_repr_css_set_property (css, "text-anchor", "start");
5014 sp_repr_css_set_property (css, "text-align", "start");
5015 break;
5016 }
5017 case 1:
5018 {
5019 sp_repr_css_set_property (css, "text-anchor", "middle");
5020 sp_repr_css_set_property (css, "text-align", "center");
5021 break;
5022 }
5024 case 2:
5025 {
5026 sp_repr_css_set_property (css, "text-anchor", "end");
5027 sp_repr_css_set_property (css, "text-align", "end");
5028 break;
5029 }
5031 case 3:
5032 {
5033 sp_repr_css_set_property (css, "text-anchor", "start");
5034 sp_repr_css_set_property (css, "text-align", "justify");
5035 break;
5036 }
5037 }
5039 SPStyle *query =
5040 sp_style_new (SP_ACTIVE_DOCUMENT);
5041 int result_numbers =
5042 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5044 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5045 if (result_numbers == QUERY_STYLE_NOTHING)
5046 {
5047 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5048 }
5050 sp_style_unref(query);
5052 sp_desktop_set_style (desktop, css, true, true);
5053 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5054 _("Text: Change alignment"));
5055 sp_repr_css_attr_unref (css);
5057 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5058 }
5060 void
5061 sp_text_toolbox_style_toggled (GtkToggleButton *button,
5062 gpointer data)
5063 {
5064 if (g_object_get_data (G_OBJECT (button), "block")) return;
5066 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5067 SPCSSAttr *css = sp_repr_css_attr_new ();
5068 int prop = GPOINTER_TO_INT(data);
5069 bool active = gtk_toggle_button_get_active (button);
5071 SPStyle *query =
5072 sp_style_new (SP_ACTIVE_DOCUMENT);
5074 int result_fontspec =
5075 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5077 //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5078 //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5079 //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5081 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
5082 Glib::ustring newFontSpec = "";
5084 if (fontSpec.empty()) {
5085 // Construct a new font specification if it does not yet exist
5086 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5087 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5088 fontFromStyle->Unref();
5089 }
5091 switch (prop)
5092 {
5093 case 0:
5094 {
5095 if (!fontSpec.empty()) {
5096 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5097 }
5098 if (fontSpec != newFontSpec) {
5099 // Don't even set the bold if the font didn't exist on the system
5100 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5101 }
5102 break;
5103 }
5105 case 1:
5106 {
5107 if (!fontSpec.empty()) {
5108 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5109 }
5110 if (fontSpec != newFontSpec) {
5111 // Don't even set the italic if the font didn't exist on the system
5112 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5113 }
5114 break;
5115 }
5116 }
5118 if (!newFontSpec.empty()) {
5119 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5120 }
5122 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5123 if (result_fontspec == QUERY_STYLE_NOTHING)
5124 {
5125 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5126 }
5128 sp_style_unref(query);
5130 sp_desktop_set_style (desktop, css, true, true);
5131 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5132 _("Text: Change font style"));
5133 sp_repr_css_attr_unref (css);
5135 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5136 }
5138 void
5139 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5140 gpointer data)
5141 {
5142 if (g_object_get_data (G_OBJECT (button), "block")) {
5143 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5144 return;
5145 }
5147 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5148 SPCSSAttr *css = sp_repr_css_attr_new ();
5149 int prop = GPOINTER_TO_INT(data);
5151 switch (prop)
5152 {
5153 case 0:
5154 {
5155 sp_repr_css_set_property (css, "writing-mode", "lr");
5156 break;
5157 }
5159 case 1:
5160 {
5161 sp_repr_css_set_property (css, "writing-mode", "tb");
5162 break;
5163 }
5164 }
5166 SPStyle *query =
5167 sp_style_new (SP_ACTIVE_DOCUMENT);
5168 int result_numbers =
5169 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5171 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5172 if (result_numbers == QUERY_STYLE_NOTHING)
5173 {
5174 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5175 }
5177 sp_desktop_set_style (desktop, css, true, true);
5178 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5179 _("Text: Change orientation"));
5180 sp_repr_css_attr_unref (css);
5182 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5183 }
5185 gboolean
5186 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5187 {
5188 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5189 if (!desktop) return FALSE;
5191 switch (get_group0_keyval (event)) {
5192 case GDK_Escape: // defocus
5193 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5194 sp_text_toolbox_selection_changed (NULL, tbl); // update
5195 return TRUE; // I consumed the event
5196 break;
5197 }
5198 return FALSE;
5199 }
5201 gboolean
5202 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5203 {
5204 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5205 if (!desktop) return FALSE;
5207 switch (get_group0_keyval (event)) {
5208 case GDK_KP_Enter:
5209 case GDK_Return:
5210 case GDK_Escape: // defocus
5211 gtk_widget_hide (w);
5212 popdown_visible = false;
5213 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5214 return TRUE; // I consumed the event
5215 break;
5216 case GDK_w:
5217 case GDK_W:
5218 if (event->state & GDK_CONTROL_MASK) {
5219 gtk_widget_hide (w);
5220 popdown_visible = false;
5221 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5222 return TRUE; // I consumed the event
5223 }
5224 break;
5225 }
5226 return FALSE;
5227 }
5230 void
5231 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5232 GObject *tbl)
5233 {
5234 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5236 if (g_object_get_data (tbl, "size-block")) return;
5238 // If this is not from selecting a size in the list (in which case get_active will give the
5239 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5240 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5241 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5242 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5243 return;
5245 gchar *endptr;
5246 gdouble value = -1;
5247 char *text = gtk_combo_box_get_active_text (cbox);
5248 if (text) {
5249 value = g_strtod (text, &endptr);
5250 if (endptr == text) // conversion failed, non-numeric input
5251 value = -1;
5252 free (text);
5253 }
5254 if (value <= 0) {
5255 return; // could not parse value
5256 }
5258 SPCSSAttr *css = sp_repr_css_attr_new ();
5259 Inkscape::CSSOStringStream osfs;
5260 osfs << value;
5261 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5263 SPStyle *query =
5264 sp_style_new (SP_ACTIVE_DOCUMENT);
5265 int result_numbers =
5266 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5268 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5269 if (result_numbers == QUERY_STYLE_NOTHING)
5270 {
5271 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5272 }
5274 sp_style_unref(query);
5276 sp_desktop_set_style (desktop, css, true, true);
5277 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5278 _("Text: Change font size"));
5279 sp_repr_css_attr_unref (css);
5281 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5282 }
5284 gboolean
5285 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5286 {
5287 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5288 if (!desktop) return FALSE;
5290 if (!g_object_get_data (tbl, "esc-pressed")) {
5291 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5292 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5293 sp_text_toolbox_size_changed (cbox, tbl);
5294 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5295 }
5296 return FALSE; // I consumed the event
5297 }
5300 gboolean
5301 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5302 {
5303 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5304 if (!desktop) return FALSE;
5306 switch (get_group0_keyval (event)) {
5307 case GDK_Escape: // defocus
5308 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5309 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5310 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5311 return TRUE; // I consumed the event
5312 break;
5313 case GDK_Return: // defocus
5314 case GDK_KP_Enter:
5315 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5316 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5317 sp_text_toolbox_size_changed (cbox, tbl);
5318 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5319 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5320 return TRUE; // I consumed the event
5321 break;
5322 }
5323 return FALSE;
5324 }
5326 void
5327 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
5328 GObject *tbl)
5329 {
5330 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5331 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5332 int x, y;
5334 if (!popdown_visible)
5335 {
5336 gdk_window_get_origin (widget->window, &x, &y);
5337 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5338 gtk_widget_show_all (popdown);
5339 //sp_transientize (popdown);
5341 gdk_pointer_grab (widget->window, TRUE,
5342 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5343 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5344 GDK_POINTER_MOTION_MASK),
5345 NULL, NULL, GDK_CURRENT_TIME);
5347 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5349 popdown_visible = true;
5350 }
5351 else
5352 {
5353 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5354 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5355 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5356 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5357 gtk_widget_hide (popdown);
5358 popdown_visible = false;
5359 }
5360 }
5362 gboolean
5363 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
5364 GdkEventFocus */*event*/,
5365 GObject */*tbl*/)
5366 {
5367 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5368 return FALSE;
5369 }
5371 gboolean
5372 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
5373 GdkEventFocus */*event*/,
5374 GObject */*tbl*/)
5375 {
5376 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5378 if (popdown_hasfocus) {
5379 gtk_widget_hide (popdown);
5380 popdown_hasfocus = false;
5381 popdown_visible = false;
5382 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5383 return TRUE;
5384 }
5385 return FALSE;
5386 }
5388 gboolean
5389 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
5390 GdkEventFocus */*event*/,
5391 GObject */*tbl*/)
5392 {
5393 popdown_hasfocus = true;
5394 return TRUE;
5395 }
5398 void
5399 cell_data_func (GtkTreeViewColumn */*column*/,
5400 GtkCellRenderer *cell,
5401 GtkTreeModel *tree_model,
5402 GtkTreeIter *iter,
5403 gpointer /*data*/)
5404 {
5405 char *family,
5406 *family_escaped,
5407 *sample_escaped;
5409 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5411 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5413 family_escaped = g_markup_escape_text (family, -1);
5414 sample_escaped = g_markup_escape_text (sample, -1);
5416 std::stringstream markup;
5417 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5418 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5420 free (family);
5421 free (family_escaped);
5422 free (sample_escaped);
5423 }
5425 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5426 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5427 if (completion) {
5428 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5429 g_object_unref (completion);
5430 }
5431 }
5433 GtkWidget*
5434 sp_text_toolbox_new (SPDesktop *desktop)
5435 {
5436 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5437 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5439 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5440 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5442 GtkTooltips *tt = gtk_tooltips_new();
5443 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5445 ////////////Family
5446 //Window
5447 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5448 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5450 //Entry
5451 GtkWidget *entry = gtk_entry_new ();
5452 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5453 GtkEntryCompletion *completion = gtk_entry_completion_new ();
5454 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5455 gtk_entry_completion_set_text_column (completion, 0);
5456 gtk_entry_completion_set_minimum_key_length (completion, 1);
5457 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5458 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5459 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5460 gtk_toolbar_append_widget( tbl, entry, "", "" );
5461 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5463 //Button
5464 GtkWidget *button = gtk_button_new ();
5465 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5466 gtk_toolbar_append_widget( tbl, button, "", "");
5468 //Popdown
5469 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
5470 GtkWidget *treeview = gtk_tree_view_new ();
5472 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5473 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
5474 gtk_tree_view_column_pack_start (column, cell, FALSE);
5475 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5476 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5477 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5479 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5480 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5481 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5483 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5485 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5486 gtk_container_add (GTK_CONTAINER (sw), treeview);
5488 gtk_container_add (GTK_CONTAINER (window), sw);
5489 gtk_widget_set_size_request (window, 300, 450);
5491 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5492 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5493 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5495 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5497 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5498 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5499 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5501 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5502 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5504 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5505 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5506 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5507 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5508 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5510 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5511 GtkWidget *box = gtk_event_box_new ();
5512 gtk_container_add (GTK_CONTAINER (box), image);
5513 gtk_toolbar_append_widget( tbl, box, "", "");
5514 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5515 GtkTooltips *tooltips = gtk_tooltips_new ();
5516 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5517 gtk_widget_hide (GTK_WIDGET (box));
5518 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5520 ////////////Size
5521 const char *sizes[] = {
5522 "4", "6", "8", "9", "10", "11", "12", "13", "14",
5523 "16", "18", "20", "22", "24", "28",
5524 "32", "36", "40", "48", "56", "64", "72", "144"
5525 };
5527 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5528 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5529 gtk_widget_set_size_request (cbox, 80, -1);
5530 gtk_toolbar_append_widget( tbl, cbox, "", "");
5531 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5532 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5533 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5534 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5536 ////////////Text anchor
5537 GtkWidget *group = gtk_radio_button_new (NULL);
5538 GtkWidget *row = gtk_hbox_new (FALSE, 4);
5539 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5541 // left
5542 GtkWidget *rbutton = group;
5543 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5544 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5545 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5547 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5548 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
5549 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5550 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5552 // center
5553 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5554 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5555 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5556 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5558 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5559 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
5560 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5561 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5563 // right
5564 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5565 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5566 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5567 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5569 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5570 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5571 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5572 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5574 // fill
5575 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5576 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5577 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5578 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5580 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5581 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
5582 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5583 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5585 gtk_toolbar_append_widget( tbl, row, "", "");
5587 //spacer
5588 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5590 ////////////Text style
5591 row = gtk_hbox_new (FALSE, 4);
5593 // bold
5594 rbutton = gtk_toggle_button_new ();
5595 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5596 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5597 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5598 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5600 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5601 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
5602 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5604 // italic
5605 rbutton = gtk_toggle_button_new ();
5606 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5607 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5608 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5609 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5611 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5612 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
5613 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5615 gtk_toolbar_append_widget( tbl, row, "", "");
5617 //spacer
5618 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5620 ////////////Text orientation
5621 group = gtk_radio_button_new (NULL);
5622 row = gtk_hbox_new (FALSE, 4);
5623 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5625 // horizontal
5626 rbutton = group;
5627 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5628 gtk_container_add (GTK_CONTAINER (rbutton),
5629 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5630 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5631 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5633 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5634 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5635 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5637 // vertical
5638 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5639 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5640 gtk_container_add (GTK_CONTAINER (rbutton),
5641 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5642 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5643 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5645 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5646 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
5647 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5648 gtk_toolbar_append_widget( tbl, row, "", "" );
5651 //watch selection
5652 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5654 sigc::connection *c_selection_changed =
5655 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5656 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5657 pool->add_connection ("selection-changed", c_selection_changed);
5659 sigc::connection *c_selection_modified =
5660 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5661 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5662 pool->add_connection ("selection-modified", c_selection_modified);
5664 sigc::connection *c_subselection_changed =
5665 new sigc::connection (desktop->connectToolSubselectionChanged
5666 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5667 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5669 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5672 gtk_widget_show_all( GTK_WIDGET(tbl) );
5674 return GTK_WIDGET(tbl);
5675 } // end of sp_text_toolbox_new()
5677 }//<unnamed> namespace
5680 //#########################
5681 //## Connector ##
5682 //#########################
5684 static void sp_connector_path_set_avoid(void)
5685 {
5686 cc_selection_set_avoid(true);
5687 }
5690 static void sp_connector_path_set_ignore(void)
5691 {
5692 cc_selection_set_avoid(false);
5693 }
5697 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5698 {
5699 // quit if run by the _changed callbacks
5700 if (g_object_get_data( tbl, "freeze" )) {
5701 return;
5702 }
5704 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5705 SPDocument *doc = sp_desktop_document(desktop);
5707 if (!sp_document_get_undo_sensitive(doc))
5708 {
5709 return;
5710 }
5712 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5714 if ( repr->attribute("inkscape:connector-spacing") ) {
5715 gdouble priorValue = gtk_adjustment_get_value(adj);
5716 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5717 if ( priorValue == gtk_adjustment_get_value(adj) ) {
5718 return;
5719 }
5720 } else if ( adj->value == defaultConnSpacing ) {
5721 return;
5722 }
5724 // in turn, prevent callbacks from responding
5725 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5727 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5728 SP_OBJECT(desktop->namedview)->updateRepr();
5730 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5731 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5732 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5733 NR::Matrix m = NR::identity();
5734 avoid_item_move(&m, item);
5735 }
5737 if (items) {
5738 g_slist_free(items);
5739 }
5741 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5742 _("Change connector spacing"));
5744 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5746 spinbutton_defocus(GTK_OBJECT(tbl));
5747 }
5749 static void sp_connector_graph_layout(void)
5750 {
5751 if (!SP_ACTIVE_DESKTOP) return;
5753 // hack for clones, see comment in align-and-distribute.cpp
5754 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5755 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5757 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5759 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5761 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5762 }
5764 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5765 {
5766 if ( gtk_toggle_action_get_active( act ) ) {
5767 prefs_set_string_attribute("tools.connector", "directedlayout",
5768 "true");
5769 } else {
5770 prefs_set_string_attribute("tools.connector", "directedlayout",
5771 "false");
5772 }
5773 }
5775 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5776 {
5777 if ( gtk_toggle_action_get_active( act ) ) {
5778 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5779 "true");
5780 } else {
5781 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5782 "false");
5783 }
5784 }
5787 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5788 {
5789 prefs_set_double_attribute("tools.connector", "length", adj->value);
5790 spinbutton_defocus(GTK_OBJECT(tbl));
5791 }
5793 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5794 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5795 bool /*is_interactive*/, gpointer data)
5796 {
5797 GtkWidget *tbl = GTK_WIDGET(data);
5799 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5800 return;
5801 }
5802 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5803 return;
5804 }
5806 GtkAdjustment *adj = (GtkAdjustment*)
5807 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5808 gdouble spacing = defaultConnSpacing;
5809 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5811 gtk_adjustment_set_value(adj, spacing);
5812 }
5815 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5816 NULL, /* child_added */
5817 NULL, /* child_removed */
5818 connector_tb_event_attr_changed,
5819 NULL, /* content_changed */
5820 NULL /* order_changed */
5821 };
5824 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5825 {
5826 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5828 {
5829 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5830 _("Avoid"),
5831 _("Make connectors avoid selected objects"),
5832 "connector_avoid",
5833 secondarySize );
5834 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5835 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5836 }
5838 {
5839 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5840 _("Ignore"),
5841 _("Make connectors ignore selected objects"),
5842 "connector_ignore",
5843 secondarySize );
5844 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5845 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5846 }
5848 EgeAdjustmentAction* eact = 0;
5850 // Spacing spinbox
5851 eact = create_adjustment_action( "ConnectorSpacingAction",
5852 _("Connector Spacing"), _("Spacing:"),
5853 _("The amount of space left around objects by auto-routing connectors"),
5854 "tools.connector", "spacing", defaultConnSpacing,
5855 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5856 0, 100, 1.0, 10.0,
5857 0, 0, 0,
5858 connector_spacing_changed, 1, 0 );
5859 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5861 // Graph (connector network) layout
5862 {
5863 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5864 _("Graph"),
5865 _("Nicely arrange selected connector network"),
5866 "graph_layout",
5867 secondarySize );
5868 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5869 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5870 }
5872 // Default connector length spinbox
5873 eact = create_adjustment_action( "ConnectorLengthAction",
5874 _("Connector Length"), _("Length:"),
5875 _("Ideal length for connectors when layout is applied"),
5876 "tools.connector", "length", 100,
5877 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5878 10, 1000, 10.0, 100.0,
5879 0, 0, 0,
5880 connector_length_changed, 1, 0 );
5881 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5884 // Directed edges toggle button
5885 {
5886 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5887 _("Downwards"),
5888 _("Make connectors with end-markers (arrows) point downwards"),
5889 "directed_graph",
5890 Inkscape::ICON_SIZE_DECORATION );
5891 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5893 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5894 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5895 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5897 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5898 }
5900 // Avoid overlaps toggle button
5901 {
5902 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5903 _("Remove overlaps"),
5904 _("Do not allow overlapping shapes"),
5905 "remove_overlaps",
5906 Inkscape::ICON_SIZE_DECORATION );
5907 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5909 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5910 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5911 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5913 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5914 }
5916 // Code to watch for changes to the connector-spacing attribute in
5917 // the XML.
5918 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5919 g_assert(repr != NULL);
5921 purge_repr_listener( holder, holder );
5923 if (repr) {
5924 g_object_set_data( holder, "repr", repr );
5925 Inkscape::GC::anchor(repr);
5926 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5927 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5928 }
5929 } // end of sp_connector_toolbox_prep()
5932 //#########################
5933 //## Paintbucket ##
5934 //#########################
5936 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5937 {
5938 gint channels = ege_select_one_action_get_active( act );
5939 flood_channels_set_channels( channels );
5940 }
5942 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5943 {
5944 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5945 }
5947 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5948 {
5949 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5950 }
5952 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5953 {
5954 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5955 SPUnit const *unit = tracker->getActiveUnit();
5957 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5959 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5960 }
5962 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5963 {
5964 // FIXME: make defaults settable via Inkscape Options
5965 struct KeyValue {
5966 char const *key;
5967 double value;
5968 } const key_values[] = {
5969 {"threshold", 15},
5970 {"offset", 0.0}
5971 };
5973 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5974 KeyValue const &kv = key_values[i];
5975 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5976 if ( adj ) {
5977 gtk_adjustment_set_value(adj, kv.value);
5978 }
5979 }
5981 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5982 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5983 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5984 ege_select_one_action_set_active( autogap_action, 0 );
5985 }
5987 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5988 {
5989 EgeAdjustmentAction* eact = 0;
5991 {
5992 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5994 GList* items = 0;
5995 gint count = 0;
5996 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5997 {
5998 GtkTreeIter iter;
5999 gtk_list_store_append( model, &iter );
6000 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6001 count++;
6002 }
6003 g_list_free( items );
6004 items = 0;
6005 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6006 g_object_set( act1, "short_label", _("Fill by:"), NULL );
6007 ege_select_one_action_set_appearance( act1, "compact" );
6008 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6009 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6010 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6011 g_object_set_data( holder, "channels_action", act1 );
6012 }
6014 // Spacing spinbox
6015 {
6016 eact = create_adjustment_action(
6017 "ThresholdAction",
6018 _("Fill Threshold"), _("Threshold:"),
6019 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6020 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6021 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6022 0, 0, 0,
6023 paintbucket_threshold_changed, 1, 0 );
6025 ege_adjustment_action_set_appearance( eact, "full" );
6026 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6027 }
6029 // Create the units menu.
6030 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6031 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6032 if (stored_unit)
6033 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6034 g_object_set_data( holder, "tracker", tracker );
6035 {
6036 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6037 gtk_action_group_add_action( mainActions, act );
6038 }
6040 // Offset spinbox
6041 {
6042 eact = create_adjustment_action(
6043 "OffsetAction",
6044 _("Grow/shrink by"), _("Grow/shrink by:"),
6045 _("The amount to grow (positive) or shrink (negative) the created fill path"),
6046 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6047 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6048 0, 0, 0,
6049 paintbucket_offset_changed, 1, 2);
6050 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6052 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6053 }
6055 /* Auto Gap */
6056 {
6057 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6059 GList* items = 0;
6060 gint count = 0;
6061 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6062 {
6063 GtkTreeIter iter;
6064 gtk_list_store_append( model, &iter );
6065 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6066 count++;
6067 }
6068 g_list_free( items );
6069 items = 0;
6070 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6071 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6072 ege_select_one_action_set_appearance( act2, "compact" );
6073 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6074 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6075 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6076 g_object_set_data( holder, "autogap_action", act2 );
6077 }
6079 /* Reset */
6080 {
6081 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6082 _("Defaults"),
6083 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6084 GTK_STOCK_CLEAR );
6085 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6086 gtk_action_group_add_action( mainActions, act );
6087 gtk_action_set_sensitive( act, TRUE );
6088 }
6090 }
6092 /*
6093 Local Variables:
6094 mode:c++
6095 c-file-style:"stroustrup"
6096 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6097 indent-tabs-mode:nil
6098 fill-column:99
6099 End:
6100 */
6101 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :