76bdc7d07acbba61cdac7d69b57103fa60412f80
1 /** \file
2 * Controls bars for some of Inkscape's tools
3 * (for some tools, they are in their own files)
4 */
6 /*
7 *
8 * Authors:
9 * MenTaLguY <mental@rydia.net>
10 * Lauris Kaplinski <lauris@kaplinski.com>
11 * bulia byak <buliabyak@users.sf.net>
12 * Frank Felfe <innerspace@iname.com>
13 * John Cliff <simarilius@yahoo.com>
14 * David Turner <novalis@gnu.org>
15 * Josh Andler <scislac@scislac.com>
16 * Jon A. Cruz <jon@joncruz.org>
17 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2006 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <cstring>
31 #include <string>
33 #include <gtkmm.h>
34 #include <gtk/gtk.h>
35 #include <iostream>
36 #include <sstream>
38 #include "widgets/button.h"
39 #include "widgets/widget-sizes.h"
40 #include "widgets/spw-utilities.h"
41 #include "widgets/spinbutton-events.h"
42 #include "dialogs/text-edit.h"
43 #include "dialogs/dialog-events.h"
45 #include "ui/widget/style-swatch.h"
47 #include "prefs-utils.h"
48 #include "verbs.h"
49 #include "sp-namedview.h"
50 #include "desktop.h"
51 #include "desktop-handles.h"
52 #include "xml/repr.h"
53 #include "xml/node-event-vector.h"
54 #include <glibmm/i18n.h>
55 #include "helper/unit-menu.h"
56 #include "helper/units.h"
58 #include "inkscape.h"
59 #include "conn-avoid-ref.h"
62 #include "select-toolbar.h"
63 #include "gradient-toolbar.h"
65 #include "connector-context.h"
66 #include "node-context.h"
67 #include "shape-editor.h"
68 #include "tweak-context.h"
69 #include "sp-rect.h"
70 #include "box3d.h"
71 #include "box3d-context.h"
72 #include "sp-star.h"
73 #include "sp-spiral.h"
74 #include "sp-ellipse.h"
75 #include "sp-text.h"
76 #include "sp-flowtext.h"
77 #include "sp-clippath.h"
78 #include "sp-mask.h"
79 #include "style.h"
80 #include "selection.h"
81 #include "selection-chemistry.h"
82 #include "document-private.h"
83 #include "desktop-style.h"
84 #include "../libnrtype/font-lister.h"
85 #include "../libnrtype/font-instance.h"
86 #include "../connection-pool.h"
87 #include "../prefs-utils.h"
88 #include "../inkscape-stock.h"
89 #include "icon.h"
90 #include "graphlayout/graphlayout.h"
92 #include "mod360.h"
94 #include "toolbox.h"
96 #include "flood-context.h"
98 #include "ink-action.h"
99 #include "ege-adjustment-action.h"
100 #include "ege-output-action.h"
101 #include "ege-select-one-action.h"
102 #include "helper/unit-tracker.h"
104 #include "svg/css-ostringstream.h"
106 #include "widgets/calligraphic-profile-rename.h"
108 using Inkscape::UnitTracker;
110 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
111 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
113 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
126 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
132 static struct {
133 gchar const *type_name;
134 gchar const *data_name;
135 sp_verb_t verb;
136 sp_verb_t doubleclick_verb;
137 } const tools[] = {
138 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
139 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
140 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
141 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
142 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
143 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
144 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
145 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
146 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
147 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
148 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
149 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
150 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
151 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
152 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
153 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
154 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
155 { NULL, NULL, 0, 0 }
156 };
158 static struct {
159 gchar const *type_name;
160 gchar const *data_name;
161 GtkWidget *(*create_func)(SPDesktop *desktop);
162 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
163 gchar const *ui_name;
164 gint swatch_verb_id;
165 gchar const *swatch_tool;
166 gchar const *swatch_tip;
167 } const aux_toolboxes[] = {
168 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
169 SP_VERB_INVALID, 0, 0},
170 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
171 SP_VERB_INVALID, 0, 0},
172 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
173 SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
174 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
175 SP_VERB_INVALID, 0, 0},
176 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
177 SP_VERB_CONTEXT_STAR_PREFS, "tools.shapes.star", _("Style of new stars")},
178 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
179 SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", _("Style of new rectangles")},
180 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
181 SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", _("Style of new 3D boxes")},
182 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
183 SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", _("Style of new ellipses")},
184 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
185 SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral", _("Style of new spirals")},
186 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
187 SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
188 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
189 SP_VERB_CONTEXT_PEN_PREFS, "tools.freehand.pen", _("Style of new paths created by Pen")},
190 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
191 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
192 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
193 SP_VERB_INVALID, 0, 0},
194 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
195 SP_VERB_INVALID, 0, 0},
196 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
197 SP_VERB_INVALID, 0, 0},
198 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
199 SP_VERB_INVALID, 0, 0},
200 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
201 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
202 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
203 };
206 static gchar const * ui_descr =
207 "<ui>"
208 " <toolbar name='SelectToolbar'>"
209 " <toolitem action='EditSelectAll' />"
210 " <toolitem action='EditSelectAllInAllLayers' />"
211 " <toolitem action='EditDeselect' />"
212 " <separator />"
213 " <toolitem action='ObjectRotate90CCW' />"
214 " <toolitem action='ObjectRotate90' />"
215 " <toolitem action='ObjectFlipHorizontally' />"
216 " <toolitem action='ObjectFlipVertically' />"
217 " <separator />"
218 " <toolitem action='SelectionToBack' />"
219 " <toolitem action='SelectionLower' />"
220 " <toolitem action='SelectionRaise' />"
221 " <toolitem action='SelectionToFront' />"
222 " <separator />"
223 " <toolitem action='XAction' />"
224 " <toolitem action='YAction' />"
225 " <toolitem action='WidthAction' />"
226 " <toolitem action='LockAction' />"
227 " <toolitem action='HeightAction' />"
228 " <toolitem action='UnitsAction' />"
229 " <separator />"
230 " <toolitem action='transform_affect_label' />"
231 " <toolitem action='transform_stroke' />"
232 " <toolitem action='transform_corners' />"
233 " <toolitem action='transform_gradient' />"
234 " <toolitem action='transform_pattern' />"
235 " </toolbar>"
237 " <toolbar name='NodeToolbar'>"
238 " <toolitem action='NodeInsertAction' />"
239 " <toolitem action='NodeDeleteAction' />"
240 " <separator />"
241 " <toolitem action='NodeJoinAction' />"
242 " <toolitem action='NodeBreakAction' />"
243 " <separator />"
244 " <toolitem action='NodeJoinSegmentAction' />"
245 " <toolitem action='NodeDeleteSegmentAction' />"
246 " <separator />"
247 " <toolitem action='NodeCuspAction' />"
248 " <toolitem action='NodeSmoothAction' />"
249 " <toolitem action='NodeSymmetricAction' />"
250 " <separator />"
251 " <toolitem action='NodeLineAction' />"
252 " <toolitem action='NodeCurveAction' />"
253 " <separator />"
254 " <toolitem action='ObjectToPath' />"
255 " <toolitem action='StrokeToPath' />"
256 " <separator />"
257 " <toolitem action='NodeXAction' />"
258 " <toolitem action='NodeYAction' />"
259 " <toolitem action='NodeUnitsAction' />"
260 " <separator />"
261 " <toolitem action='ObjectEditClipPathAction' />"
262 " <toolitem action='ObjectEditMaskPathAction' />"
263 " <toolitem action='EditNextLPEParameterAction' />"
264 " <separator />"
265 " <toolitem action='NodesShowHandlesAction' />"
266 " <toolitem action='NodesShowHelperpath' />"
267 " </toolbar>"
269 " <toolbar name='TweakToolbar'>"
270 " <toolitem action='TweakWidthAction' />"
271 " <separator />"
272 " <toolitem action='TweakForceAction' />"
273 " <toolitem action='TweakPressureAction' />"
274 " <separator />"
275 " <toolitem action='TweakModeAction' />"
276 " <separator />"
277 " <toolitem action='TweakFidelityAction' />"
278 " <separator />"
279 " <toolitem action='TweakChannelsLabel' />"
280 " <toolitem action='TweakDoH' />"
281 " <toolitem action='TweakDoS' />"
282 " <toolitem action='TweakDoL' />"
283 " <toolitem action='TweakDoO' />"
284 " </toolbar>"
286 " <toolbar name='ZoomToolbar'>"
287 " <toolitem action='ZoomIn' />"
288 " <toolitem action='ZoomOut' />"
289 " <separator />"
290 " <toolitem action='Zoom1:0' />"
291 " <toolitem action='Zoom1:2' />"
292 " <toolitem action='Zoom2:1' />"
293 " <separator />"
294 " <toolitem action='ZoomSelection' />"
295 " <toolitem action='ZoomDrawing' />"
296 " <toolitem action='ZoomPage' />"
297 " <toolitem action='ZoomPageWidth' />"
298 " <separator />"
299 " <toolitem action='ZoomPrev' />"
300 " <toolitem action='ZoomNext' />"
301 " </toolbar>"
303 " <toolbar name='StarToolbar'>"
304 " <separator />"
305 " <toolitem action='StarStateAction' />"
306 " <separator />"
307 " <toolitem action='FlatAction' />"
308 " <separator />"
309 " <toolitem action='MagnitudeAction' />"
310 " <toolitem action='SpokeAction' />"
311 " <toolitem action='RoundednessAction' />"
312 " <toolitem action='RandomizationAction' />"
313 " <separator />"
314 " <toolitem action='StarResetAction' />"
315 " </toolbar>"
317 " <toolbar name='RectToolbar'>"
318 " <toolitem action='RectStateAction' />"
319 " <toolitem action='RectWidthAction' />"
320 " <toolitem action='RectHeightAction' />"
321 " <toolitem action='RadiusXAction' />"
322 " <toolitem action='RadiusYAction' />"
323 " <toolitem action='RectUnitsAction' />"
324 " <separator />"
325 " <toolitem action='RectResetAction' />"
326 " </toolbar>"
328 " <toolbar name='3DBoxToolbar'>"
329 " <toolitem action='3DBoxAngleXAction' />"
330 " <toolitem action='3DBoxVPXStateAction' />"
331 " <separator />"
332 " <toolitem action='3DBoxAngleYAction' />"
333 " <toolitem action='3DBoxVPYStateAction' />"
334 " <separator />"
335 " <toolitem action='3DBoxAngleZAction' />"
336 " <toolitem action='3DBoxVPZStateAction' />"
337 " </toolbar>"
339 " <toolbar name='SpiralToolbar'>"
340 " <toolitem action='SpiralStateAction' />"
341 " <toolitem action='SpiralRevolutionAction' />"
342 " <toolitem action='SpiralExpansionAction' />"
343 " <toolitem action='SpiralT0Action' />"
344 " <separator />"
345 " <toolitem action='SpiralResetAction' />"
346 " </toolbar>"
348 " <toolbar name='PenToolbar'>"
349 " </toolbar>"
351 " <toolbar name='PencilToolbar'>"
352 " </toolbar>"
354 " <toolbar name='CalligraphyToolbar'>"
355 " <separator />"
356 " <toolitem action='SetProfileAction'/>"
357 " <toolitem action='SaveDeleteProfileAction'/>"
358 " <separator />"
359 " <toolitem action='CalligraphyWidthAction' />"
360 " <toolitem action='PressureAction' />"
361 " <toolitem action='TraceAction' />"
362 " <toolitem action='ThinningAction' />"
363 " <separator />"
364 " <toolitem action='AngleAction' />"
365 " <toolitem action='TiltAction' />"
366 " <toolitem action='FixationAction' />"
367 " <separator />"
368 " <toolitem action='CapRoundingAction' />"
369 " <separator />"
370 " <toolitem action='TremorAction' />"
371 " <toolitem action='WiggleAction' />"
372 " <toolitem action='MassAction' />"
373 " <separator />"
374 " </toolbar>"
376 " <toolbar name='ArcToolbar'>"
377 " <toolitem action='ArcStateAction' />"
378 " <separator />"
379 " <toolitem action='ArcStartAction' />"
380 " <toolitem action='ArcEndAction' />"
381 " <separator />"
382 " <toolitem action='ArcOpenAction' />"
383 " <separator />"
384 " <toolitem action='ArcResetAction' />"
385 " <separator />"
386 " </toolbar>"
388 " <toolbar name='PaintbucketToolbar'>"
389 " <toolitem action='ChannelsAction' />"
390 " <separator />"
391 " <toolitem action='ThresholdAction' />"
392 " <separator />"
393 " <toolitem action='OffsetAction' />"
394 " <toolitem action='PaintbucketUnitsAction' />"
395 " <separator />"
396 " <toolitem action='AutoGapAction' />"
397 " <separator />"
398 " <toolitem action='PaintbucketResetAction' />"
399 " </toolbar>"
401 " <toolbar name='DropperToolbar'>"
402 " <toolitem action='DropperOpacityAction' />"
403 " <toolitem action='DropperPickAlphaAction' />"
404 " <toolitem action='DropperSetAlphaAction' />"
405 " </toolbar>"
407 " <toolbar name='ConnectorToolbar'>"
408 " <toolitem action='ConnectorAvoidAction' />"
409 " <toolitem action='ConnectorIgnoreAction' />"
410 " <toolitem action='ConnectorSpacingAction' />"
411 " <toolitem action='ConnectorGraphAction' />"
412 " <toolitem action='ConnectorLengthAction' />"
413 " <toolitem action='ConnectorDirectedAction' />"
414 " <toolitem action='ConnectorOverlapAction' />"
415 " </toolbar>"
417 "</ui>"
418 ;
420 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
422 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
424 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
425 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
427 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
428 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
430 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
431 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
433 /* Global text entry widgets necessary for update */
434 /* GtkWidget *dropper_rgb_entry,
435 *dropper_opacity_entry ; */
436 // should be made a private member once this is converted to class
438 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
439 connection->disconnect();
440 delete connection;
441 }
443 static void purge_repr_listener( GObject* obj, GObject* tbl )
444 {
445 (void)obj;
446 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
447 if (oldrepr) { // remove old listener
448 sp_repr_remove_listener_by_data(oldrepr, tbl);
449 Inkscape::GC::release(oldrepr);
450 oldrepr = 0;
451 g_object_set_data( tbl, "repr", NULL );
452 }
453 }
455 GtkWidget *
456 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
457 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
458 Inkscape::UI::View::View *view, GtkTooltips *tt)
459 {
460 SPAction *action = verb->get_action(view);
461 if (!action) return NULL;
463 SPAction *doubleclick_action;
464 if (doubleclick_verb)
465 doubleclick_action = doubleclick_verb->get_action(view);
466 else
467 doubleclick_action = NULL;
469 /* fixme: Handle sensitive/unsensitive */
470 /* fixme: Implement sp_button_new_from_action */
471 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
472 gtk_widget_show(b);
473 gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
475 return b;
476 }
478 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
479 Inkscape::UI::View::View *view, GtkTooltips *tt)
480 {
481 return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
482 }
484 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
485 Inkscape::UI::View::View *view, GtkTooltips *tt)
486 {
487 return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
488 }
491 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
492 {
493 SPAction* targetAction = SP_ACTION(user_data);
494 if ( targetAction ) {
495 sp_action_perform( targetAction, NULL );
496 }
497 }
499 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
500 {
501 if ( data ) {
502 GtkAction* act = GTK_ACTION(data);
503 gtk_action_set_sensitive( act, sensitive );
504 }
505 }
507 static SPActionEventVector action_event_vector = {
508 {NULL},
509 NULL,
510 NULL,
511 sp_action_action_set_sensitive,
512 NULL,
513 NULL
514 };
516 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
517 {
518 GtkAction* act = 0;
520 SPAction* targetAction = verb->get_action(view);
521 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
522 act = GTK_ACTION(inky);
523 gtk_action_set_sensitive( act, targetAction->sensitive );
525 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
527 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
528 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
530 return act;
531 }
533 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
534 {
535 Inkscape::UI::View::View *view = desktop;
536 gint verbsToUse[] = {
537 // disabled until we have icons for them:
538 //find
539 //SP_VERB_EDIT_TILE,
540 //SP_VERB_EDIT_UNTILE,
541 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
542 SP_VERB_DIALOG_DISPLAY,
543 SP_VERB_DIALOG_FILL_STROKE,
544 SP_VERB_DIALOG_NAMEDVIEW,
545 SP_VERB_DIALOG_TEXT,
546 SP_VERB_DIALOG_XML_EDITOR,
547 SP_VERB_EDIT_CLONE,
548 SP_VERB_EDIT_COPY,
549 SP_VERB_EDIT_CUT,
550 SP_VERB_EDIT_DUPLICATE,
551 SP_VERB_EDIT_PASTE,
552 SP_VERB_EDIT_REDO,
553 SP_VERB_EDIT_UNDO,
554 SP_VERB_EDIT_UNLINK_CLONE,
555 SP_VERB_FILE_EXPORT,
556 SP_VERB_FILE_IMPORT,
557 SP_VERB_FILE_NEW,
558 SP_VERB_FILE_OPEN,
559 SP_VERB_FILE_PRINT,
560 SP_VERB_FILE_SAVE,
561 SP_VERB_OBJECT_TO_CURVE,
562 SP_VERB_SELECTION_GROUP,
563 SP_VERB_SELECTION_OUTLINE,
564 SP_VERB_SELECTION_UNGROUP,
565 SP_VERB_ZOOM_1_1,
566 SP_VERB_ZOOM_1_2,
567 SP_VERB_ZOOM_2_1,
568 SP_VERB_ZOOM_DRAWING,
569 SP_VERB_ZOOM_IN,
570 SP_VERB_ZOOM_NEXT,
571 SP_VERB_ZOOM_OUT,
572 SP_VERB_ZOOM_PAGE,
573 SP_VERB_ZOOM_PAGE_WIDTH,
574 SP_VERB_ZOOM_PREV,
575 SP_VERB_ZOOM_SELECTION,
576 };
578 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
579 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
581 static std::map<SPDesktop*, GtkActionGroup*> groups;
582 GtkActionGroup* mainActions = 0;
583 if ( groups.find(desktop) != groups.end() ) {
584 mainActions = groups[desktop];
585 }
587 if ( !mainActions ) {
588 mainActions = gtk_action_group_new("main");
589 groups[desktop] = mainActions;
590 }
592 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
593 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
594 if ( verb ) {
595 if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
596 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
597 gtk_action_group_add_action( mainActions, act );
598 }
599 }
600 }
602 return mainActions;
603 }
606 GtkWidget *
607 sp_tool_toolbox_new()
608 {
609 GtkTooltips *tt = gtk_tooltips_new();
610 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
612 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
613 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
615 gtk_widget_set_sensitive(tb, FALSE);
617 GtkWidget *hb = gtk_handle_box_new();
618 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
619 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
620 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
622 gtk_container_add(GTK_CONTAINER(hb), tb);
623 gtk_widget_show(GTK_WIDGET(tb));
625 sigc::connection* conn = new sigc::connection;
626 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
628 return hb;
629 }
631 static void
632 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
633 {
634 g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
635 gtk_widget_queue_resize(child);
636 }
638 static void
639 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
640 {
641 g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
642 gtk_widget_queue_resize(child);
643 }
645 GtkWidget *
646 sp_aux_toolbox_new()
647 {
648 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
650 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
652 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
654 gtk_widget_set_sensitive(tb, FALSE);
656 GtkWidget *hb = gtk_handle_box_new();
657 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
658 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
659 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
661 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
662 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
664 gtk_container_add(GTK_CONTAINER(hb), tb);
665 gtk_widget_show(GTK_WIDGET(tb));
667 sigc::connection* conn = new sigc::connection;
668 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
670 return hb;
671 }
673 //####################################
674 //# Commands Bar
675 //####################################
677 GtkWidget *
678 sp_commands_toolbox_new()
679 {
680 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
682 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
684 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
685 gtk_widget_set_sensitive(tb, FALSE);
687 GtkWidget *hb = gtk_handle_box_new();
688 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
689 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
690 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
692 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
693 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
695 gtk_container_add(GTK_CONTAINER(hb), tb);
696 gtk_widget_show(GTK_WIDGET(tb));
698 sigc::connection* conn = new sigc::connection;
699 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
701 return hb;
702 }
704 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
705 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
706 gchar const *path, gchar const *data, gdouble def,
707 GtkWidget *focusTarget,
708 GtkWidget *us,
709 GObject *dataKludge,
710 gboolean altx, gchar const *altx_mark,
711 gdouble lower, gdouble upper, gdouble step, gdouble page,
712 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
713 void (*callback)(GtkAdjustment *, GObject *),
714 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
715 {
716 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
717 lower, upper, step, page, page ) );
718 if (us) {
719 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
720 }
722 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
724 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
725 if ( shortLabel ) {
726 g_object_set( act, "short_label", shortLabel, NULL );
727 }
729 if ( (descrCount > 0) && descrLabels && descrValues ) {
730 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
731 }
733 if ( focusTarget ) {
734 ege_adjustment_action_set_focuswidget( act, focusTarget );
735 }
737 if ( altx && altx_mark ) {
738 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
739 }
741 if ( dataKludge ) {
742 g_object_set_data( dataKludge, data, adj );
743 }
745 // Using a cast just to make sure we pass in the right kind of function pointer
746 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
748 return act;
749 }
752 //####################################
753 //# node editing callbacks
754 //####################################
756 /**
757 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
758 */
759 static ShapeEditor *get_current_shape_editor()
760 {
761 if (!SP_ACTIVE_DESKTOP) {
762 return NULL;
763 }
765 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
767 if (!SP_IS_NODE_CONTEXT(event_context)) {
768 return NULL;
769 }
771 return SP_NODE_CONTEXT(event_context)->shape_editor;
772 }
775 void
776 sp_node_path_edit_add(void)
777 {
778 ShapeEditor *shape_editor = get_current_shape_editor();
779 if (shape_editor) shape_editor->add_node();
780 }
782 void
783 sp_node_path_edit_delete(void)
784 {
785 ShapeEditor *shape_editor = get_current_shape_editor();
786 if (shape_editor) shape_editor->delete_nodes();
787 }
789 void
790 sp_node_path_edit_delete_segment(void)
791 {
792 ShapeEditor *shape_editor = get_current_shape_editor();
793 if (shape_editor) shape_editor->delete_segment();
794 }
796 void
797 sp_node_path_edit_break(void)
798 {
799 ShapeEditor *shape_editor = get_current_shape_editor();
800 if (shape_editor) shape_editor->break_at_nodes();
801 }
803 void
804 sp_node_path_edit_join(void)
805 {
806 ShapeEditor *shape_editor = get_current_shape_editor();
807 if (shape_editor) shape_editor->join_nodes();
808 }
810 void
811 sp_node_path_edit_join_segment(void)
812 {
813 ShapeEditor *shape_editor = get_current_shape_editor();
814 if (shape_editor) shape_editor->join_segments();
815 }
817 void
818 sp_node_path_edit_toline(void)
819 {
820 ShapeEditor *shape_editor = get_current_shape_editor();
821 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
822 }
824 void
825 sp_node_path_edit_tocurve(void)
826 {
827 ShapeEditor *shape_editor = get_current_shape_editor();
828 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
829 }
831 void
832 sp_node_path_edit_cusp(void)
833 {
834 ShapeEditor *shape_editor = get_current_shape_editor();
835 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
836 }
838 void
839 sp_node_path_edit_smooth(void)
840 {
841 ShapeEditor *shape_editor = get_current_shape_editor();
842 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
843 }
845 void
846 sp_node_path_edit_symmetrical(void)
847 {
848 ShapeEditor *shape_editor = get_current_shape_editor();
849 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
850 }
852 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
853 bool show = gtk_toggle_action_get_active( act );
854 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
855 ShapeEditor *shape_editor = get_current_shape_editor();
856 if (shape_editor) shape_editor->show_handles(show);
857 }
859 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
860 bool show = gtk_toggle_action_get_active( act );
861 prefs_set_int_attribute ("tools.nodes", "show_helperpath", show ? 1 : 0);
862 ShapeEditor *shape_editor = get_current_shape_editor();
863 if (shape_editor) shape_editor->show_helperpath(show);
864 }
866 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
867 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
868 }
870 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
871 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
872 }
874 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
875 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
876 }
878 /* is called when the node selection is modified */
879 static void
880 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
881 {
882 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
883 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
884 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
885 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
887 // quit if run by the attr_changed listener
888 if (g_object_get_data( tbl, "freeze" )) {
889 return;
890 }
892 // in turn, prevent listener from responding
893 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
895 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
896 SPUnit const *unit = tracker->getActiveUnit();
898 ShapeEditor *shape_editor = get_current_shape_editor();
899 if (shape_editor && shape_editor->has_nodepath()) {
900 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
901 int n_selected = 0;
902 if (nodepath) {
903 n_selected = nodepath->numSelected();
904 }
906 if (n_selected == 0) {
907 gtk_action_set_sensitive(xact, FALSE);
908 gtk_action_set_sensitive(yact, FALSE);
909 } else {
910 gtk_action_set_sensitive(xact, TRUE);
911 gtk_action_set_sensitive(yact, TRUE);
912 NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
913 NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
915 if (n_selected == 1) {
916 NR::Point sel_node = nodepath->singleSelectedCoords();
917 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
918 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
919 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
920 }
921 } else {
922 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
923 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
924 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
925 /* Note: Currently x and y will always have a value, even if the coordinates of the
926 selected nodes don't coincide (in this case we use the coordinates of the center
927 of the bounding box). So the entries are never set to zero. */
928 // FIXME: Maybe we should clear the entry if several nodes are selected
929 // instead of providing a kind of average value
930 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
931 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
932 }
933 }
934 }
935 } else {
936 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
937 gtk_action_set_sensitive(xact, FALSE);
938 gtk_action_set_sensitive(yact, FALSE);
939 }
941 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
942 }
944 static void
945 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
946 {
947 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
949 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
950 SPUnit const *unit = tracker->getActiveUnit();
952 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
953 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
954 }
956 // quit if run by the attr_changed listener
957 if (g_object_get_data( tbl, "freeze" )) {
958 return;
959 }
961 // in turn, prevent listener from responding
962 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
964 ShapeEditor *shape_editor = get_current_shape_editor();
965 if (shape_editor && shape_editor->has_nodepath()) {
966 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
967 if (!strcmp(value_name, "x")) {
968 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
969 }
970 if (!strcmp(value_name, "y")) {
971 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
972 }
973 }
975 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
976 }
978 static void
979 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
980 {
981 sp_node_path_value_changed(adj, tbl, "x");
982 }
984 static void
985 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
986 {
987 sp_node_path_value_changed(adj, tbl, "y");
988 }
990 void
991 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
992 {
993 {
994 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
995 SPItem *item = selection->singleItem();
996 if (item && SP_IS_SHAPE(item)) {
997 LivePathEffectObject *lpeobj = sp_shape_get_livepatheffectobject(SP_SHAPE(item));
998 if (lpeobj) {
999 gtk_action_set_sensitive(w, TRUE);
1000 } else {
1001 gtk_action_set_sensitive(w, FALSE);
1002 }
1003 } else {
1004 gtk_action_set_sensitive(w, FALSE);
1005 }
1006 }
1008 {
1009 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1010 SPItem *item = selection->singleItem();
1011 if (item && item->clip_ref && item->clip_ref->getObject()) {
1012 gtk_action_set_sensitive(w, TRUE);
1013 } else {
1014 gtk_action_set_sensitive(w, FALSE);
1015 }
1016 }
1018 {
1019 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1020 SPItem *item = selection->singleItem();
1021 if (item && item->mask_ref && item->mask_ref->getObject()) {
1022 gtk_action_set_sensitive(w, TRUE);
1023 } else {
1024 gtk_action_set_sensitive(w, FALSE);
1025 }
1026 }
1027 }
1029 void
1030 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1031 {
1032 sp_node_toolbox_sel_changed (selection, tbl);
1033 }
1037 //################################
1038 //## Node Editing Toolbox ##
1039 //################################
1041 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1042 {
1043 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1044 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1045 g_object_set_data( holder, "tracker", tracker );
1047 {
1048 InkAction* inky = ink_action_new( "NodeInsertAction",
1049 _("Insert node"),
1050 _("Insert new nodes into selected segments"),
1051 "node_insert",
1052 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1053 g_object_set( inky, "short_label", _("Insert"), NULL );
1054 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1055 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1056 }
1058 {
1059 InkAction* inky = ink_action_new( "NodeDeleteAction",
1060 _("Delete node"),
1061 _("Delete selected nodes"),
1062 "node_delete",
1063 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1064 g_object_set( inky, "short_label", _("Delete"), NULL );
1065 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1066 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1067 }
1069 {
1070 InkAction* inky = ink_action_new( "NodeJoinAction",
1071 _("Join endnodes"),
1072 _("Join selected endnodes"),
1073 "node_join",
1074 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1075 g_object_set( inky, "short_label", _("Join"), NULL );
1076 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1077 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1078 }
1080 {
1081 InkAction* inky = ink_action_new( "NodeBreakAction",
1082 _("Break nodes"),
1083 _("Break path at selected nodes"),
1084 "node_break",
1085 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1086 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1087 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1088 }
1091 {
1092 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1093 _("Join with segment"),
1094 _("Join selected endnodes with a new segment"),
1095 "node_join_segment",
1096 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1097 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1098 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1099 }
1101 {
1102 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1103 _("Delete segment"),
1104 _("Delete segment between two non-endpoint nodes"),
1105 "node_delete_segment",
1106 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1107 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1108 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1109 }
1111 {
1112 InkAction* inky = ink_action_new( "NodeCuspAction",
1113 _("Node Cusp"),
1114 _("Make selected nodes corner"),
1115 "node_cusp",
1116 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1117 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1118 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1119 }
1121 {
1122 InkAction* inky = ink_action_new( "NodeSmoothAction",
1123 _("Node Smooth"),
1124 _("Make selected nodes smooth"),
1125 "node_smooth",
1126 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1127 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1128 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1129 }
1131 {
1132 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1133 _("Node Symmetric"),
1134 _("Make selected nodes symmetric"),
1135 "node_symmetric",
1136 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1137 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1138 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1139 }
1141 {
1142 InkAction* inky = ink_action_new( "NodeLineAction",
1143 _("Node Line"),
1144 _("Make selected segments lines"),
1145 "node_line",
1146 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1147 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1148 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1149 }
1151 {
1152 InkAction* inky = ink_action_new( "NodeCurveAction",
1153 _("Node Curve"),
1154 _("Make selected segments curves"),
1155 "node_curve",
1156 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1157 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1158 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1159 }
1161 {
1162 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1163 _("Show Handles"),
1164 _("Show the Bezier handles of selected nodes"),
1165 "nodes_show_handles",
1166 Inkscape::ICON_SIZE_DECORATION );
1167 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1168 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1169 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1170 }
1172 {
1173 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1174 _("Show Outline"),
1175 _("Show the outline of the path"),
1176 "nodes_show_helperpath",
1177 Inkscape::ICON_SIZE_DECORATION );
1178 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1179 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1180 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1181 }
1183 {
1184 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1185 _("Next path effect parameter"),
1186 _("Show next path effect parameter for editing"),
1187 "edit_next_parameter",
1188 Inkscape::ICON_SIZE_DECORATION );
1189 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1190 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1191 g_object_set_data( holder, "nodes_lpeedit", inky);
1192 }
1194 {
1195 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1196 _("Edit clipping path"),
1197 _("Edit the clipping path of the object"),
1198 "nodeedit-clippath",
1199 Inkscape::ICON_SIZE_DECORATION );
1200 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1201 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1202 g_object_set_data( holder, "nodes_clippathedit", inky);
1203 }
1205 {
1206 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1207 _("Edit mask path"),
1208 _("Edit the mask of the object"),
1209 "nodeedit-mask",
1210 Inkscape::ICON_SIZE_DECORATION );
1211 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1212 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1213 g_object_set_data( holder, "nodes_maskedit", inky);
1214 }
1216 /* X coord of selected node(s) */
1217 {
1218 EgeAdjustmentAction* eact = 0;
1219 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1220 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1221 eact = create_adjustment_action( "NodeXAction",
1222 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1223 "tools.nodes", "Xcoord", 0,
1224 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1225 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1226 labels, values, G_N_ELEMENTS(labels),
1227 sp_node_path_x_value_changed );
1228 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1229 g_object_set_data( holder, "nodes_x_action", eact );
1230 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1231 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1232 }
1234 /* Y coord of selected node(s) */
1235 {
1236 EgeAdjustmentAction* eact = 0;
1237 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1238 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1239 eact = create_adjustment_action( "NodeYAction",
1240 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1241 "tools.nodes", "Ycoord", 0,
1242 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1243 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1244 labels, values, G_N_ELEMENTS(labels),
1245 sp_node_path_y_value_changed );
1246 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1247 g_object_set_data( holder, "nodes_y_action", eact );
1248 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1249 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1250 }
1252 // add the units menu
1253 {
1254 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1255 gtk_action_group_add_action( mainActions, act );
1256 }
1259 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1261 //watch selection
1262 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1264 sigc::connection *c_selection_changed =
1265 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1266 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1267 pool->add_connection ("selection-changed", c_selection_changed);
1269 sigc::connection *c_selection_modified =
1270 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1271 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1272 pool->add_connection ("selection-modified", c_selection_modified);
1274 sigc::connection *c_subselection_changed =
1275 new sigc::connection (desktop->connectToolSubselectionChanged
1276 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1277 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1279 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1281 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1282 } // end of sp_node_toolbox_prep()
1285 //########################
1286 //## Zoom Toolbox ##
1287 //########################
1289 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1290 {
1291 // no custom GtkAction setup needed
1292 } // end of sp_zoom_toolbox_prep()
1294 void
1295 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1296 {
1297 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)), desktop, setup_tool_toolbox, update_tool_toolbox, static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), "event_context_connection")));
1298 }
1301 void
1302 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1303 {
1304 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)), desktop, setup_aux_toolbox, update_aux_toolbox, static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), "event_context_connection")));
1305 }
1307 void
1308 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1309 {
1310 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)), desktop, setup_commands_toolbox, update_commands_toolbox, static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), "event_context_connection")));
1311 }
1313 static void
1314 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1315 {
1316 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1317 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1319 if (old_desktop) {
1320 GList *children, *iter;
1322 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1323 for ( iter = children ; iter ; iter = iter->next ) {
1324 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1325 }
1326 g_list_free(children);
1327 }
1329 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1331 if (desktop) {
1332 gtk_widget_set_sensitive(toolbox, TRUE);
1333 setup_func(toolbox, desktop);
1334 update_func(desktop, desktop->event_context, toolbox);
1335 *conn = desktop->connectEventContextChanged
1336 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1337 } else {
1338 gtk_widget_set_sensitive(toolbox, FALSE);
1339 }
1341 } // end of toolbox_set_desktop()
1344 static void
1345 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1346 {
1347 GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1348 gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1349 if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1350 // "toolbox.tools" was not set. Fallback to older value
1351 shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1353 // Copy the setting forwards
1354 prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1355 }
1356 Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1358 for (int i = 0 ; tools[i].type_name ; i++ ) {
1359 GtkWidget *button =
1360 sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1361 SP_BUTTON_TYPE_TOGGLE,
1362 Inkscape::Verb::get(tools[i].verb),
1363 Inkscape::Verb::get(tools[i].doubleclick_verb),
1364 desktop,
1365 tooltips );
1367 g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1368 (gpointer)button );
1369 }
1370 }
1373 static void
1374 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1375 {
1376 gchar const *const tname = ( eventcontext
1377 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1378 : NULL );
1379 for (int i = 0 ; tools[i].type_name ; i++ ) {
1380 SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1381 sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1382 }
1383 }
1385 static void
1386 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1387 {
1388 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1389 GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1390 GtkUIManager* mgr = gtk_ui_manager_new();
1391 GError* errVal = 0;
1392 gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1393 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1395 std::map<std::string, GtkWidget*> dataHolders;
1397 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1398 if ( aux_toolboxes[i].prep_func ) {
1399 // converted to GtkActions and UIManager
1401 GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1402 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1403 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1404 dataHolders[aux_toolboxes[i].type_name] = kludge;
1405 aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1406 } else {
1408 GtkWidget *sub_toolbox = 0;
1409 if (aux_toolboxes[i].create_func == NULL)
1410 sub_toolbox = sp_empty_toolbox_new(desktop);
1411 else {
1412 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1413 }
1415 gtk_size_group_add_widget( grouper, sub_toolbox );
1417 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1418 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1420 }
1421 }
1423 // Second pass to create toolbars *after* all GtkActions are created
1424 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1425 if ( aux_toolboxes[i].prep_func ) {
1426 // converted to GtkActions and UIManager
1428 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1430 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1431 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1433 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1434 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1435 g_free( tmp );
1436 tmp = 0;
1438 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1439 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1440 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1441 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1442 }
1443 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1446 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1448 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1449 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1450 swatch->setDesktop( desktop );
1451 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1452 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1453 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1454 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 );
1455 }
1457 gtk_widget_show_all( holder );
1458 sp_set_font_size_smaller( holder );
1460 gtk_size_group_add_widget( grouper, holder );
1462 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1463 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1464 }
1465 }
1467 g_object_unref( G_OBJECT(grouper) );
1468 }
1470 static void
1471 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1472 {
1473 gchar const *tname = ( eventcontext
1474 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1475 : NULL );
1476 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1477 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1478 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1479 gtk_widget_show_all(sub_toolbox);
1480 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1481 } else {
1482 gtk_widget_hide(sub_toolbox);
1483 }
1484 }
1485 }
1487 static void
1488 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1489 {
1490 gchar const * descr =
1491 "<ui>"
1492 " <toolbar name='CommandsToolbar'>"
1493 " <toolitem action='FileNew' />"
1494 " <toolitem action='FileOpen' />"
1495 " <toolitem action='FileSave' />"
1496 " <toolitem action='FilePrint' />"
1497 " <separator />"
1498 " <toolitem action='FileImport' />"
1499 " <toolitem action='FileExport' />"
1500 " <separator />"
1501 " <toolitem action='EditUndo' />"
1502 " <toolitem action='EditRedo' />"
1503 " <separator />"
1504 " <toolitem action='EditCopy' />"
1505 " <toolitem action='EditCut' />"
1506 " <toolitem action='EditPaste' />"
1507 " <separator />"
1508 " <toolitem action='ZoomSelection' />"
1509 " <toolitem action='ZoomDrawing' />"
1510 " <toolitem action='ZoomPage' />"
1511 " <separator />"
1512 " <toolitem action='EditDuplicate' />"
1513 " <toolitem action='EditClone' />"
1514 " <toolitem action='EditUnlinkClone' />"
1515 " <separator />"
1516 " <toolitem action='SelectionGroup' />"
1517 " <toolitem action='SelectionUnGroup' />"
1518 " <separator />"
1519 " <toolitem action='DialogFillStroke' />"
1520 " <toolitem action='DialogText' />"
1521 " <toolitem action='DialogXMLEditor' />"
1522 " <toolitem action='DialogAlignDistribute' />"
1523 " <separator />"
1524 " <toolitem action='DialogPreferences' />"
1525 " <toolitem action='DialogDocumentProperties' />"
1526 " </toolbar>"
1527 "</ui>";
1528 GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1531 GtkUIManager* mgr = gtk_ui_manager_new();
1532 GError* errVal = 0;
1534 gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1535 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1537 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1538 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1539 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1540 }
1541 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1542 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1543 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1546 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1547 }
1549 static void
1550 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1551 {
1552 }
1554 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1555 {
1556 gtk_widget_show(toolbox_toplevel);
1557 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1559 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1560 if (!shown_toolbox) {
1561 return;
1562 }
1563 gtk_widget_show(toolbox);
1565 gtk_widget_show_all(shown_toolbox);
1566 }
1568 void
1569 aux_toolbox_space(GtkWidget *tb, gint space)
1570 {
1571 gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1572 }
1574 static GtkWidget *
1575 sp_empty_toolbox_new(SPDesktop *desktop)
1576 {
1577 GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1578 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1579 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1581 gtk_widget_show_all(tbl);
1582 sp_set_font_size_smaller (tbl);
1584 return tbl;
1585 }
1587 // helper UI functions
1589 GtkWidget *
1590 sp_tb_spinbutton(
1591 gchar *label, gchar const *tooltip,
1592 gchar const *path, gchar const *data, gdouble def,
1593 GtkWidget *us,
1594 GtkWidget *tbl,
1595 gboolean altx, gchar const *altx_mark,
1596 gdouble lower, gdouble upper, gdouble step, gdouble page,
1597 void (*callback)(GtkAdjustment *, GtkWidget *),
1598 gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1599 {
1600 GtkTooltips *tt = gtk_tooltips_new();
1602 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1604 GtkWidget *l = gtk_label_new(label);
1605 gtk_widget_show(l);
1606 gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1607 gtk_container_add(GTK_CONTAINER(hb), l);
1609 GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1610 lower, upper, step, page, page);
1611 gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1612 if (us)
1613 sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1615 GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1616 gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1617 if (altx)
1618 gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1619 gtk_widget_set_size_request(sb,
1620 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1621 AUX_SPINBUTTON_HEIGHT);
1622 gtk_widget_show(sb);
1623 gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1624 gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1625 gtk_container_add(GTK_CONTAINER(hb), sb);
1626 gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1628 return hb;
1629 }
1631 #define MODE_LABEL_WIDTH 70
1633 //########################
1634 //## Star ##
1635 //########################
1637 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1638 {
1639 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1641 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1642 // do not remember prefs if this call is initiated by an undo change, because undoing object
1643 // creation sets bogus values to its attributes before it is deleted
1644 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1645 }
1647 // quit if run by the attr_changed listener
1648 if (g_object_get_data( dataKludge, "freeze" )) {
1649 return;
1650 }
1652 // in turn, prevent listener from responding
1653 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1655 bool modmade = false;
1657 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1658 GSList const *items = selection->itemList();
1659 for (; items != NULL; items = items->next) {
1660 if (SP_IS_STAR((SPItem *) items->data)) {
1661 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1662 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1663 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1664 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1665 + M_PI / (gint)adj->value));
1666 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1667 modmade = true;
1668 }
1669 }
1670 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1671 _("Star: Change number of corners"));
1673 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1674 }
1676 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1677 {
1678 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1680 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1681 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1682 }
1684 // quit if run by the attr_changed listener
1685 if (g_object_get_data( dataKludge, "freeze" )) {
1686 return;
1687 }
1689 // in turn, prevent listener from responding
1690 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1692 bool modmade = false;
1693 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1694 GSList const *items = selection->itemList();
1695 for (; items != NULL; items = items->next) {
1696 if (SP_IS_STAR((SPItem *) items->data)) {
1697 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1699 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1700 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1701 if (r2 < r1) {
1702 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1703 } else {
1704 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1705 }
1707 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1708 modmade = true;
1709 }
1710 }
1712 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1713 _("Star: Change spoke ratio"));
1715 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1716 }
1718 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1719 {
1720 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1721 bool flat = ege_select_one_action_get_active( act ) == 0;
1723 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1724 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1725 flat ? "true" : "false" );
1726 }
1728 // quit if run by the attr_changed listener
1729 if (g_object_get_data( dataKludge, "freeze" )) {
1730 return;
1731 }
1733 // in turn, prevent listener from responding
1734 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1736 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1737 GSList const *items = selection->itemList();
1738 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1739 bool modmade = false;
1741 if ( prop_action ) {
1742 gtk_action_set_sensitive( prop_action, !flat );
1743 }
1745 for (; items != NULL; items = items->next) {
1746 if (SP_IS_STAR((SPItem *) items->data)) {
1747 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1748 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1749 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1750 modmade = true;
1751 }
1752 }
1754 if (modmade) {
1755 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1756 flat ? _("Make polygon") : _("Make star"));
1757 }
1759 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1760 }
1762 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1763 {
1764 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1766 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1767 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1768 }
1770 // quit if run by the attr_changed listener
1771 if (g_object_get_data( dataKludge, "freeze" )) {
1772 return;
1773 }
1775 // in turn, prevent listener from responding
1776 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1778 bool modmade = false;
1780 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1781 GSList const *items = selection->itemList();
1782 for (; items != NULL; items = items->next) {
1783 if (SP_IS_STAR((SPItem *) items->data)) {
1784 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1785 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1786 SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1787 modmade = true;
1788 }
1789 }
1790 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1791 _("Star: Change rounding"));
1793 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1794 }
1796 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1797 {
1798 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1800 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1801 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1802 }
1804 // quit if run by the attr_changed listener
1805 if (g_object_get_data( dataKludge, "freeze" )) {
1806 return;
1807 }
1809 // in turn, prevent listener from responding
1810 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1812 bool modmade = false;
1814 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1815 GSList const *items = selection->itemList();
1816 for (; items != NULL; items = items->next) {
1817 if (SP_IS_STAR((SPItem *) items->data)) {
1818 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1819 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1820 SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1821 modmade = true;
1822 }
1823 }
1824 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1825 _("Star: Change randomization"));
1827 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1828 }
1831 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1832 gchar const */*old_value*/, gchar const */*new_value*/,
1833 bool /*is_interactive*/, gpointer data)
1834 {
1835 GtkWidget *tbl = GTK_WIDGET(data);
1837 // quit if run by the _changed callbacks
1838 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1839 return;
1840 }
1842 // in turn, prevent callbacks from responding
1843 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1845 GtkAdjustment *adj = 0;
1847 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1848 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1850 if (!strcmp(name, "inkscape:randomized")) {
1851 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1852 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1853 } else if (!strcmp(name, "inkscape:rounded")) {
1854 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1855 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1856 } else if (!strcmp(name, "inkscape:flatsided")) {
1857 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1858 char const *flatsides = repr->attribute("inkscape:flatsided");
1859 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1860 if ( flatsides && !strcmp(flatsides,"false") ) {
1861 ege_select_one_action_set_active( flat_action, 1 );
1862 gtk_action_set_sensitive( prop_action, TRUE );
1863 } else {
1864 ege_select_one_action_set_active( flat_action, 0 );
1865 gtk_action_set_sensitive( prop_action, FALSE );
1866 }
1867 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1868 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1869 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1870 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1871 if (r2 < r1) {
1872 gtk_adjustment_set_value(adj, r2/r1);
1873 } else {
1874 gtk_adjustment_set_value(adj, r1/r2);
1875 }
1876 } else if (!strcmp(name, "sodipodi:sides")) {
1877 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1878 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1879 }
1881 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1882 }
1885 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1886 {
1887 NULL, /* child_added */
1888 NULL, /* child_removed */
1889 star_tb_event_attr_changed,
1890 NULL, /* content_changed */
1891 NULL /* order_changed */
1892 };
1895 /**
1896 * \param selection Should not be NULL.
1897 */
1898 static void
1899 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1900 {
1901 int n_selected = 0;
1902 Inkscape::XML::Node *repr = NULL;
1904 purge_repr_listener( tbl, tbl );
1906 for (GSList const *items = selection->itemList();
1907 items != NULL;
1908 items = items->next)
1909 {
1910 if (SP_IS_STAR((SPItem *) items->data)) {
1911 n_selected++;
1912 repr = SP_OBJECT_REPR((SPItem *) items->data);
1913 }
1914 }
1916 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1918 if (n_selected == 0) {
1919 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1920 } else if (n_selected == 1) {
1921 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1923 if (repr) {
1924 g_object_set_data( tbl, "repr", repr );
1925 Inkscape::GC::anchor(repr);
1926 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1927 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1928 }
1929 } else {
1930 // FIXME: implement averaging of all parameters for multiple selected stars
1931 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1932 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1933 }
1934 }
1937 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1938 {
1939 // FIXME: in this and all other _default functions, set some flag telling the value_changed
1940 // callbacks to lump all the changes for all selected objects in one undo step
1942 GtkAdjustment *adj = 0;
1944 // fixme: make settable in prefs!
1945 gint mag = 5;
1946 gdouble prop = 0.5;
1947 gboolean flat = FALSE;
1948 gdouble randomized = 0;
1949 gdouble rounded = 0;
1951 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1952 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1954 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1955 gtk_action_set_sensitive( sb2, !flat );
1957 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1958 gtk_adjustment_set_value(adj, mag);
1959 gtk_adjustment_value_changed(adj);
1961 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1962 gtk_adjustment_set_value(adj, prop);
1963 gtk_adjustment_value_changed(adj);
1965 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1966 gtk_adjustment_set_value(adj, rounded);
1967 gtk_adjustment_value_changed(adj);
1969 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1970 gtk_adjustment_set_value(adj, randomized);
1971 gtk_adjustment_value_changed(adj);
1972 }
1975 void
1976 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1977 {
1978 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1979 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1980 GtkWidget *l = gtk_label_new(NULL);
1981 gtk_label_set_markup(GTK_LABEL(l), title);
1982 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1983 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1984 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1985 }
1988 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1989 {
1990 {
1991 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1992 ege_output_action_set_use_markup( act, TRUE );
1993 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1994 g_object_set_data( holder, "mode_action", act );
1995 }
1997 {
1998 EgeAdjustmentAction* eact = 0;
1999 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2000 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2002 /* Flatsided checkbox */
2003 {
2004 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2006 GtkTreeIter iter;
2007 gtk_list_store_append( model, &iter );
2008 gtk_list_store_set( model, &iter,
2009 0, _("Polygon"),
2010 1, _("Regular polygon (with one handle) instead of a star"),
2011 2, "star_flat",
2012 -1 );
2014 gtk_list_store_append( model, &iter );
2015 gtk_list_store_set( model, &iter,
2016 0, _("Star"),
2017 1, _("Star instead of a regular polygon (with one handle)"),
2018 2, "star_angled",
2019 -1 );
2021 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2022 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2023 g_object_set_data( holder, "flat_action", act );
2025 ege_select_one_action_set_appearance( act, "full" );
2026 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2027 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2028 ege_select_one_action_set_icon_column( act, 2 );
2029 ege_select_one_action_set_tooltip_column( act, 1 );
2031 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2032 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2033 }
2035 /* Magnitude */
2036 {
2037 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2038 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2039 eact = create_adjustment_action( "MagnitudeAction",
2040 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2041 "tools.shapes.star", "magnitude", 3,
2042 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2043 3, 1024, 1, 5,
2044 labels, values, G_N_ELEMENTS(labels),
2045 sp_stb_magnitude_value_changed,
2046 1.0, 0 );
2047 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2048 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2049 }
2051 /* Spoke ratio */
2052 {
2053 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2054 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2055 eact = create_adjustment_action( "SpokeAction",
2056 _("Spoke ratio"), _("Spoke ratio:"),
2057 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2058 // Base radius is the same for the closest handle.
2059 _("Base radius to tip radius ratio"),
2060 "tools.shapes.star", "proportion", 0.5,
2061 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2062 0.01, 1.0, 0.01, 0.1,
2063 labels, values, G_N_ELEMENTS(labels),
2064 sp_stb_proportion_value_changed );
2065 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2066 g_object_set_data( holder, "prop_action", eact );
2067 }
2069 if ( !isFlatSided ) {
2070 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2071 } else {
2072 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2073 }
2075 /* Roundedness */
2076 {
2077 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2078 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2079 eact = create_adjustment_action( "RoundednessAction",
2080 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2081 "tools.shapes.star", "rounded", 0.0,
2082 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2083 -10.0, 10.0, 0.01, 0.1,
2084 labels, values, G_N_ELEMENTS(labels),
2085 sp_stb_rounded_value_changed );
2086 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2087 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2088 }
2090 /* Randomization */
2091 {
2092 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2093 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2094 eact = create_adjustment_action( "RandomizationAction",
2095 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2096 "tools.shapes.star", "randomized", 0.0,
2097 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2098 -10.0, 10.0, 0.001, 0.01,
2099 labels, values, G_N_ELEMENTS(labels),
2100 sp_stb_randomized_value_changed, 0.1, 3 );
2101 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2102 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2103 }
2104 }
2106 {
2107 /* Reset */
2108 {
2109 GtkAction* act = gtk_action_new( "StarResetAction",
2110 _("Defaults"),
2111 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2112 GTK_STOCK_CLEAR );
2113 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2114 gtk_action_group_add_action( mainActions, act );
2115 gtk_action_set_sensitive( act, TRUE );
2116 }
2117 }
2119 sigc::connection *connection = new sigc::connection(
2120 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2121 );
2122 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2123 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2124 }
2127 //########################
2128 //## Rect ##
2129 //########################
2131 static void sp_rtb_sensitivize( GObject *tbl )
2132 {
2133 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2134 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2135 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2137 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2138 gtk_action_set_sensitive( not_rounded, FALSE );
2139 } else {
2140 gtk_action_set_sensitive( not_rounded, TRUE );
2141 }
2142 }
2145 static void
2146 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2147 void (*setter)(SPRect *, gdouble))
2148 {
2149 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2151 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2152 SPUnit const *unit = tracker->getActiveUnit();
2154 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2155 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2156 }
2158 // quit if run by the attr_changed listener
2159 if (g_object_get_data( tbl, "freeze" )) {
2160 return;
2161 }
2163 // in turn, prevent listener from responding
2164 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2166 bool modmade = false;
2167 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2168 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2169 if (SP_IS_RECT(items->data)) {
2170 if (adj->value != 0) {
2171 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2172 } else {
2173 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2174 }
2175 modmade = true;
2176 }
2177 }
2179 sp_rtb_sensitivize( tbl );
2181 if (modmade) {
2182 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2183 _("Change rectangle"));
2184 }
2186 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2187 }
2189 static void
2190 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2191 {
2192 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2193 }
2195 static void
2196 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2197 {
2198 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2199 }
2201 static void
2202 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2203 {
2204 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2205 }
2207 static void
2208 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2209 {
2210 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2211 }
2215 static void
2216 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2217 {
2218 GtkAdjustment *adj = 0;
2220 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2221 gtk_adjustment_set_value(adj, 0.0);
2222 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2223 gtk_adjustment_value_changed(adj);
2225 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2226 gtk_adjustment_set_value(adj, 0.0);
2227 gtk_adjustment_value_changed(adj);
2229 sp_rtb_sensitivize( obj );
2230 }
2232 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2233 gchar const */*old_value*/, gchar const */*new_value*/,
2234 bool /*is_interactive*/, gpointer data)
2235 {
2236 GObject *tbl = G_OBJECT(data);
2238 // quit if run by the _changed callbacks
2239 if (g_object_get_data( tbl, "freeze" )) {
2240 return;
2241 }
2243 // in turn, prevent callbacks from responding
2244 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2246 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2247 SPUnit const *unit = tracker->getActiveUnit();
2249 gpointer item = g_object_get_data( tbl, "item" );
2250 if (item && SP_IS_RECT(item)) {
2251 {
2252 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2253 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2254 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2255 }
2257 {
2258 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2259 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2260 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2261 }
2263 {
2264 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2265 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2266 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2267 }
2269 {
2270 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2271 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2272 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2273 }
2274 }
2276 sp_rtb_sensitivize( tbl );
2278 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2279 }
2282 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2283 NULL, /* child_added */
2284 NULL, /* child_removed */
2285 rect_tb_event_attr_changed,
2286 NULL, /* content_changed */
2287 NULL /* order_changed */
2288 };
2290 /**
2291 * \param selection should not be NULL.
2292 */
2293 static void
2294 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2295 {
2296 int n_selected = 0;
2297 Inkscape::XML::Node *repr = NULL;
2298 SPItem *item = NULL;
2300 if ( g_object_get_data( tbl, "repr" ) ) {
2301 g_object_set_data( tbl, "item", NULL );
2302 }
2303 purge_repr_listener( tbl, tbl );
2305 for (GSList const *items = selection->itemList();
2306 items != NULL;
2307 items = items->next) {
2308 if (SP_IS_RECT((SPItem *) items->data)) {
2309 n_selected++;
2310 item = (SPItem *) items->data;
2311 repr = SP_OBJECT_REPR(item);
2312 }
2313 }
2315 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2317 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2319 if (n_selected == 0) {
2320 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2322 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2323 gtk_action_set_sensitive(w, FALSE);
2324 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2325 gtk_action_set_sensitive(h, FALSE);
2327 } else if (n_selected == 1) {
2328 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2329 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2331 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2332 gtk_action_set_sensitive(w, TRUE);
2333 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2334 gtk_action_set_sensitive(h, TRUE);
2336 if (repr) {
2337 g_object_set_data( tbl, "repr", repr );
2338 g_object_set_data( tbl, "item", item );
2339 Inkscape::GC::anchor(repr);
2340 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2341 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2342 }
2343 } else {
2344 // FIXME: implement averaging of all parameters for multiple selected
2345 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2346 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2347 sp_rtb_sensitivize( tbl );
2348 }
2349 }
2352 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2353 {
2354 EgeAdjustmentAction* eact = 0;
2356 {
2357 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2358 ege_output_action_set_use_markup( act, TRUE );
2359 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2360 g_object_set_data( holder, "mode_action", act );
2361 }
2363 // rx/ry units menu: create
2364 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2365 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2366 // fixme: add % meaning per cent of the width/height
2367 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2368 g_object_set_data( holder, "tracker", tracker );
2370 /* W */
2371 {
2372 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2373 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2374 eact = create_adjustment_action( "RectWidthAction",
2375 _("Width"), _("W:"), _("Width of rectangle"),
2376 "tools.shapes.rect", "width", 0,
2377 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2378 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2379 labels, values, G_N_ELEMENTS(labels),
2380 sp_rtb_width_value_changed );
2381 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2382 g_object_set_data( holder, "width_action", eact );
2383 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2384 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2385 }
2387 /* H */
2388 {
2389 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2390 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2391 eact = create_adjustment_action( "RectHeightAction",
2392 _("Height"), _("H:"), _("Height of rectangle"),
2393 "tools.shapes.rect", "height", 0,
2394 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2395 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2396 labels, values, G_N_ELEMENTS(labels),
2397 sp_rtb_height_value_changed );
2398 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2399 g_object_set_data( holder, "height_action", eact );
2400 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2401 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2402 }
2404 /* rx */
2405 {
2406 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2407 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2408 eact = create_adjustment_action( "RadiusXAction",
2409 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2410 "tools.shapes.rect", "rx", 0,
2411 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2412 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2413 labels, values, G_N_ELEMENTS(labels),
2414 sp_rtb_rx_value_changed);
2415 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2416 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2417 }
2419 /* ry */
2420 {
2421 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2422 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2423 eact = create_adjustment_action( "RadiusYAction",
2424 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2425 "tools.shapes.rect", "ry", 0,
2426 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2427 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2428 labels, values, G_N_ELEMENTS(labels),
2429 sp_rtb_ry_value_changed);
2430 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2431 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2432 }
2434 // add the units menu
2435 {
2436 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2437 gtk_action_group_add_action( mainActions, act );
2438 }
2440 /* Reset */
2441 {
2442 InkAction* inky = ink_action_new( "RectResetAction",
2443 _("Not rounded"),
2444 _("Make corners sharp"),
2445 "squared_corner",
2446 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2447 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2448 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2449 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2450 g_object_set_data( holder, "not_rounded", inky );
2451 }
2453 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2454 sp_rtb_sensitivize( holder );
2456 sigc::connection *connection = new sigc::connection(
2457 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2458 );
2459 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2460 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2461 }
2463 //########################
2464 //## 3D Box ##
2465 //########################
2467 // normalize angle so that it lies in the interval [0,360]
2468 static double box3d_normalize_angle (double a) {
2469 double angle = a + ((int) (a/360.0))*360;
2470 if (angle < 0) {
2471 angle += 360.0;
2472 }
2473 return angle;
2474 }
2476 static void
2477 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2478 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2479 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2480 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2481 // are reset).
2482 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2484 if (is_infinite) {
2485 gtk_toggle_action_set_active(tact, TRUE);
2486 gtk_action_set_sensitive(act, TRUE);
2488 double angle = persp3d_get_infinite_angle(persp, axis);
2489 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2490 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2491 }
2492 } else {
2493 gtk_toggle_action_set_active(tact, FALSE);
2494 gtk_action_set_sensitive(act, FALSE);
2495 }
2496 }
2498 static void
2499 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2500 if (!persp_repr) {
2501 g_print ("No perspective given to box3d_resync_toolbar().\n");
2502 return;
2503 }
2505 GtkWidget *tbl = GTK_WIDGET(data);
2506 GtkAdjustment *adj = 0;
2507 GtkAction *act = 0;
2508 GtkToggleAction *tact = 0;
2509 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2510 {
2511 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2512 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2513 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2515 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2516 }
2517 {
2518 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2519 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2520 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2522 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2523 }
2524 {
2525 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2526 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2527 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2529 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2530 }
2531 }
2533 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2534 gchar const */*old_value*/, gchar const */*new_value*/,
2535 bool /*is_interactive*/, gpointer data)
2536 {
2537 GtkWidget *tbl = GTK_WIDGET(data);
2539 // quit if run by the attr_changed listener
2540 // note: it used to work without the differently called freeze_ attributes (here and in
2541 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2542 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2543 return;
2544 }
2546 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2547 // sp_document_maybe_done() when the document is undo insensitive)
2548 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2550 // TODO: Only update the appropriate part of the toolbar
2551 // if (!strcmp(name, "inkscape:vp_z")) {
2552 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2553 // }
2555 Persp3D *persp = persp3d_get_from_repr(repr);
2556 persp3d_update_box_reprs(persp);
2558 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2559 }
2561 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2562 {
2563 NULL, /* child_added */
2564 NULL, /* child_removed */
2565 box3d_persp_tb_event_attr_changed,
2566 NULL, /* content_changed */
2567 NULL /* order_changed */
2568 };
2570 /**
2571 * \param selection Should not be NULL.
2572 */
2573 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2574 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2575 static void
2576 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2577 {
2578 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2579 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2580 // update the perspectives with infinite VPs and leave the other ones untouched).
2582 Inkscape::XML::Node *persp_repr = NULL;
2583 purge_repr_listener(tbl, tbl);
2585 SPItem *item = selection->singleItem();
2586 if (item && SP_IS_BOX3D(item)) {
2587 // FIXME: Also deal with multiple selected boxes
2588 SPBox3D *box = SP_BOX3D(item);
2589 Persp3D *persp = box3d_get_perspective(box);
2590 persp_repr = SP_OBJECT_REPR(persp);
2591 if (persp_repr) {
2592 g_object_set_data(tbl, "repr", persp_repr);
2593 Inkscape::GC::anchor(persp_repr);
2594 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2595 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2596 }
2598 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2599 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2601 box3d_resync_toolbar(persp_repr, tbl);
2602 }
2603 }
2605 static void
2606 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2607 {
2608 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2609 SPDocument *document = sp_desktop_document(desktop);
2611 // quit if run by the attr_changed listener
2612 // note: it used to work without the differently called freeze_ attributes (here and in
2613 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2614 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2615 return;
2616 }
2618 // in turn, prevent listener from responding
2619 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2621 //Persp3D *persp = document->current_persp3d;
2622 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2623 if (sel_persps.empty()) {
2624 // this can happen when the document is created; we silently ignore it
2625 return;
2626 }
2627 Persp3D *persp = sel_persps.front();
2629 persp->tmat.set_infinite_direction (axis, adj->value);
2630 SP_OBJECT(persp)->updateRepr();
2632 // TODO: use the correct axis here, too
2633 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2635 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2636 }
2639 static void
2640 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2641 {
2642 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2643 }
2645 static void
2646 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2647 {
2648 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2649 }
2651 static void
2652 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2653 {
2654 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2655 }
2658 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2659 {
2660 // TODO: Take all selected perspectives into account
2661 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2662 if (sel_persps.empty()) {
2663 // this can happen when the document is created; we silently ignore it
2664 return;
2665 }
2666 Persp3D *persp = sel_persps.front();
2668 bool set_infinite = gtk_toggle_action_get_active(act);
2669 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2670 }
2672 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2673 {
2674 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2675 }
2677 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2678 {
2679 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2680 }
2682 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2683 {
2684 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2685 }
2687 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2688 {
2689 EgeAdjustmentAction* eact = 0;
2690 SPDocument *document = sp_desktop_document (desktop);
2691 Persp3D *persp = document->current_persp3d;
2693 EgeAdjustmentAction* box3d_angle_x = 0;
2694 EgeAdjustmentAction* box3d_angle_y = 0;
2695 EgeAdjustmentAction* box3d_angle_z = 0;
2697 /* Angle X */
2698 {
2699 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2700 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2701 eact = create_adjustment_action( "3DBoxAngleXAction",
2702 _("Angle in X direction"), _("Angle X:"),
2703 // Translators: PL is short for 'perspective line'
2704 _("Angle of PLs in X direction"),
2705 "tools.shapes.3dbox", "box3d_angle_x", 30,
2706 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2707 -360.0, 360.0, 1.0, 10.0,
2708 labels, values, G_N_ELEMENTS(labels),
2709 box3d_angle_x_value_changed );
2710 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2711 g_object_set_data( holder, "box3d_angle_x_action", eact );
2712 box3d_angle_x = eact;
2713 }
2715 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2716 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2717 } else {
2718 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2719 }
2722 /* VP X state */
2723 {
2724 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2725 // Translators: VP is short for 'vanishing point'
2726 _("State of VP in X direction"),
2727 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2728 "toggle_vp_x",
2729 Inkscape::ICON_SIZE_DECORATION );
2730 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2731 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2732 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2733 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2734 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2735 }
2737 /* Angle Y */
2738 {
2739 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2740 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2741 eact = create_adjustment_action( "3DBoxAngleYAction",
2742 _("Angle in Y direction"), _("Angle Y:"),
2743 // Translators: PL is short for 'perspective line'
2744 _("Angle of PLs in Y direction"),
2745 "tools.shapes.3dbox", "box3d_angle_y", 30,
2746 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2747 -360.0, 360.0, 1.0, 10.0,
2748 labels, values, G_N_ELEMENTS(labels),
2749 box3d_angle_y_value_changed );
2750 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2751 g_object_set_data( holder, "box3d_angle_y_action", eact );
2752 box3d_angle_y = eact;
2753 }
2755 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2756 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2757 } else {
2758 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2759 }
2761 /* VP Y state */
2762 {
2763 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2764 // Translators: VP is short for 'vanishing point'
2765 _("State of VP in Y direction"),
2766 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2767 "toggle_vp_y",
2768 Inkscape::ICON_SIZE_DECORATION );
2769 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2770 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2771 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2772 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2773 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2774 }
2776 /* Angle Z */
2777 {
2778 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2779 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2780 eact = create_adjustment_action( "3DBoxAngleZAction",
2781 _("Angle in Z direction"), _("Angle Z:"),
2782 // Translators: PL is short for 'perspective line'
2783 _("Angle of PLs in Z direction"),
2784 "tools.shapes.3dbox", "box3d_angle_z", 30,
2785 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2786 -360.0, 360.0, 1.0, 10.0,
2787 labels, values, G_N_ELEMENTS(labels),
2788 box3d_angle_z_value_changed );
2789 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2790 g_object_set_data( holder, "box3d_angle_z_action", eact );
2791 box3d_angle_z = eact;
2792 }
2794 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2795 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2796 } else {
2797 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2798 }
2800 /* VP Z state */
2801 {
2802 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2803 // Translators: VP is short for 'vanishing point'
2804 _("State of VP in Z direction"),
2805 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2806 "toggle_vp_z",
2807 Inkscape::ICON_SIZE_DECORATION );
2808 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2809 g_object_set_data( holder, "box3d_vp_z_state_action", act );
2810 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2811 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2812 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2813 }
2815 sigc::connection *connection = new sigc::connection(
2816 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2817 );
2818 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2819 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2820 }
2822 //########################
2823 //## Spiral ##
2824 //########################
2826 static void
2827 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2828 {
2829 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2831 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2832 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2833 }
2835 // quit if run by the attr_changed listener
2836 if (g_object_get_data( tbl, "freeze" )) {
2837 return;
2838 }
2840 // in turn, prevent listener from responding
2841 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2843 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2845 bool modmade = false;
2846 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2847 items != NULL;
2848 items = items->next)
2849 {
2850 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2851 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2852 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2853 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2854 modmade = true;
2855 }
2856 }
2858 g_free(namespaced_name);
2860 if (modmade) {
2861 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2862 _("Change spiral"));
2863 }
2865 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2866 }
2868 static void
2869 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2870 {
2871 sp_spl_tb_value_changed(adj, tbl, "revolution");
2872 }
2874 static void
2875 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2876 {
2877 sp_spl_tb_value_changed(adj, tbl, "expansion");
2878 }
2880 static void
2881 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2882 {
2883 sp_spl_tb_value_changed(adj, tbl, "t0");
2884 }
2886 static void
2887 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2888 {
2889 GtkWidget *tbl = GTK_WIDGET(obj);
2891 GtkAdjustment *adj;
2893 // fixme: make settable
2894 gdouble rev = 5;
2895 gdouble exp = 1.0;
2896 gdouble t0 = 0.0;
2898 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2899 gtk_adjustment_set_value(adj, rev);
2900 gtk_adjustment_value_changed(adj);
2902 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2903 gtk_adjustment_set_value(adj, exp);
2904 gtk_adjustment_value_changed(adj);
2906 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2907 gtk_adjustment_set_value(adj, t0);
2908 gtk_adjustment_value_changed(adj);
2910 spinbutton_defocus(GTK_OBJECT(tbl));
2911 }
2914 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2915 gchar const */*old_value*/, gchar const */*new_value*/,
2916 bool /*is_interactive*/, gpointer data)
2917 {
2918 GtkWidget *tbl = GTK_WIDGET(data);
2920 // quit if run by the _changed callbacks
2921 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2922 return;
2923 }
2925 // in turn, prevent callbacks from responding
2926 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2928 GtkAdjustment *adj;
2929 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2930 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2932 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2933 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2935 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2936 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2938 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2939 }
2942 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2943 NULL, /* child_added */
2944 NULL, /* child_removed */
2945 spiral_tb_event_attr_changed,
2946 NULL, /* content_changed */
2947 NULL /* order_changed */
2948 };
2950 static void
2951 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2952 {
2953 int n_selected = 0;
2954 Inkscape::XML::Node *repr = NULL;
2956 purge_repr_listener( tbl, tbl );
2958 for (GSList const *items = selection->itemList();
2959 items != NULL;
2960 items = items->next)
2961 {
2962 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2963 n_selected++;
2964 repr = SP_OBJECT_REPR((SPItem *) items->data);
2965 }
2966 }
2968 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2970 if (n_selected == 0) {
2971 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2972 } else if (n_selected == 1) {
2973 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2975 if (repr) {
2976 g_object_set_data( tbl, "repr", repr );
2977 Inkscape::GC::anchor(repr);
2978 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2979 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2980 }
2981 } else {
2982 // FIXME: implement averaging of all parameters for multiple selected
2983 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2984 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2985 }
2986 }
2989 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2990 {
2991 EgeAdjustmentAction* eact = 0;
2993 {
2994 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2995 ege_output_action_set_use_markup( act, TRUE );
2996 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2997 g_object_set_data( holder, "mode_action", act );
2998 }
3000 /* Revolution */
3001 {
3002 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3003 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3004 eact = create_adjustment_action( "SpiralRevolutionAction",
3005 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3006 "tools.shapes.spiral", "revolution", 3.0,
3007 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3008 0.01, 1024.0, 0.1, 1.0,
3009 labels, values, G_N_ELEMENTS(labels),
3010 sp_spl_tb_revolution_value_changed, 1, 2);
3011 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3012 }
3014 /* Expansion */
3015 {
3016 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3017 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3018 eact = create_adjustment_action( "SpiralExpansionAction",
3019 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3020 "tools.shapes.spiral", "expansion", 1.0,
3021 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3022 0.0, 1000.0, 0.01, 1.0,
3023 labels, values, G_N_ELEMENTS(labels),
3024 sp_spl_tb_expansion_value_changed);
3025 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3026 }
3028 /* T0 */
3029 {
3030 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3031 gdouble values[] = {0, 0.5, 0.9};
3032 eact = create_adjustment_action( "SpiralT0Action",
3033 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3034 "tools.shapes.spiral", "t0", 0.0,
3035 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3036 0.0, 0.999, 0.01, 1.0,
3037 labels, values, G_N_ELEMENTS(labels),
3038 sp_spl_tb_t0_value_changed);
3039 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3040 }
3042 /* Reset */
3043 {
3044 InkAction* inky = ink_action_new( "SpiralResetAction",
3045 _("Defaults"),
3046 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3047 GTK_STOCK_CLEAR,
3048 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3049 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3050 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3051 }
3054 sigc::connection *connection = new sigc::connection(
3055 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3056 );
3057 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3058 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3059 }
3061 //########################
3062 //## Pen/Pencil ##
3063 //########################
3066 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
3067 {
3068 // Put stuff here
3069 }
3071 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
3072 {
3073 // Put stuff here
3074 }
3076 //########################
3077 //## Tweak ##
3078 //########################
3080 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3081 {
3082 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3083 }
3085 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3086 {
3087 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3088 }
3090 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3091 {
3092 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3093 }
3095 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3096 {
3097 int mode = ege_select_one_action_get_active( act );
3098 prefs_set_int_attribute("tools.tweak", "mode", mode);
3100 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3101 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3102 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3103 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3104 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3105 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3106 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3107 if (doh) gtk_action_set_sensitive (doh, TRUE);
3108 if (dos) gtk_action_set_sensitive (dos, TRUE);
3109 if (dol) gtk_action_set_sensitive (dol, TRUE);
3110 if (doo) gtk_action_set_sensitive (doo, TRUE);
3111 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3112 if (fid) gtk_action_set_sensitive (fid, FALSE);
3113 } else {
3114 if (doh) gtk_action_set_sensitive (doh, FALSE);
3115 if (dos) gtk_action_set_sensitive (dos, FALSE);
3116 if (dol) gtk_action_set_sensitive (dol, FALSE);
3117 if (doo) gtk_action_set_sensitive (doo, FALSE);
3118 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3119 if (fid) gtk_action_set_sensitive (fid, TRUE);
3120 }
3121 }
3123 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3124 {
3125 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3126 }
3128 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3129 bool show = gtk_toggle_action_get_active( act );
3130 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3131 }
3132 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3133 bool show = gtk_toggle_action_get_active( act );
3134 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3135 }
3136 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3137 bool show = gtk_toggle_action_get_active( act );
3138 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3139 }
3140 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3141 bool show = gtk_toggle_action_get_active( act );
3142 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3143 }
3145 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3146 {
3147 {
3148 /* Width */
3149 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3150 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3151 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3152 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3153 "tools.tweak", "width", 15,
3154 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3155 1, 100, 1.0, 10.0,
3156 labels, values, G_N_ELEMENTS(labels),
3157 sp_tweak_width_value_changed, 0.01, 0, 100 );
3158 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3159 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3160 }
3163 {
3164 /* Force */
3165 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3166 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3167 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3168 _("Force"), _("Force:"), _("The force of the tweak action"),
3169 "tools.tweak", "force", 20,
3170 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3171 1, 100, 1.0, 10.0,
3172 labels, values, G_N_ELEMENTS(labels),
3173 sp_tweak_force_value_changed, 0.01, 0, 100 );
3174 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3175 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3176 }
3178 /* Mode */
3179 {
3180 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3182 GtkTreeIter iter;
3183 gtk_list_store_append( model, &iter );
3184 gtk_list_store_set( model, &iter,
3185 0, _("Push mode"),
3186 1, _("Push parts of paths in any direction"),
3187 2, "tweak_push_mode",
3188 -1 );
3190 gtk_list_store_append( model, &iter );
3191 gtk_list_store_set( model, &iter,
3192 0, _("Shrink mode"),
3193 1, _("Shrink (inset) parts of paths"),
3194 2, "tweak_shrink_mode",
3195 -1 );
3197 gtk_list_store_append( model, &iter );
3198 gtk_list_store_set( model, &iter,
3199 0, _("Grow mode"),
3200 1, _("Grow (outset) parts of paths"),
3201 2, "tweak_grow_mode",
3202 -1 );
3204 gtk_list_store_append( model, &iter );
3205 gtk_list_store_set( model, &iter,
3206 0, _("Attract mode"),
3207 1, _("Attract parts of paths towards cursor"),
3208 2, "tweak_attract_mode",
3209 -1 );
3211 gtk_list_store_append( model, &iter );
3212 gtk_list_store_set( model, &iter,
3213 0, _("Repel mode"),
3214 1, _("Repel parts of paths from cursor"),
3215 2, "tweak_repel_mode",
3216 -1 );
3218 gtk_list_store_append( model, &iter );
3219 gtk_list_store_set( model, &iter,
3220 0, _("Roughen mode"),
3221 1, _("Roughen parts of paths"),
3222 2, "tweak_roughen_mode",
3223 -1 );
3225 gtk_list_store_append( model, &iter );
3226 gtk_list_store_set( model, &iter,
3227 0, _("Color paint mode"),
3228 1, _("Paint the tool's color upon selected objects"),
3229 2, "tweak_colorpaint_mode",
3230 -1 );
3232 gtk_list_store_append( model, &iter );
3233 gtk_list_store_set( model, &iter,
3234 0, _("Color jitter mode"),
3235 1, _("Jitter the colors of selected objects"),
3236 2, "tweak_colorjitter_mode",
3237 -1 );
3239 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3240 g_object_set( act, "short_label", _("Mode:"), NULL );
3241 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3242 g_object_set_data( holder, "mode_action", act );
3244 ege_select_one_action_set_appearance( act, "full" );
3245 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3246 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3247 ege_select_one_action_set_icon_column( act, 2 );
3248 ege_select_one_action_set_tooltip_column( act, 1 );
3250 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3251 ege_select_one_action_set_active( act, mode );
3252 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3254 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3255 }
3257 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3259 {
3260 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3261 ege_output_action_set_use_markup( act, TRUE );
3262 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3263 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3264 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3265 g_object_set_data( holder, "tweak_channels_label", act);
3266 }
3268 {
3269 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3270 _("Hue"),
3271 _("In color mode, act on objects' hue"),
3272 NULL,
3273 Inkscape::ICON_SIZE_DECORATION );
3274 g_object_set( act, "short_label", _("H"), NULL );
3275 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3276 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3277 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3278 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3279 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3280 g_object_set_data( holder, "tweak_doh", act);
3281 }
3282 {
3283 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3284 _("Saturation"),
3285 _("In color mode, act on objects' saturation"),
3286 NULL,
3287 Inkscape::ICON_SIZE_DECORATION );
3288 g_object_set( act, "short_label", _("S"), NULL );
3289 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3290 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3291 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3292 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3293 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3294 g_object_set_data( holder, "tweak_dos", act );
3295 }
3296 {
3297 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3298 _("Lightness"),
3299 _("In color mode, act on objects' lightness"),
3300 NULL,
3301 Inkscape::ICON_SIZE_DECORATION );
3302 g_object_set( act, "short_label", _("L"), NULL );
3303 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3304 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3305 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3306 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3307 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3308 g_object_set_data( holder, "tweak_dol", act );
3309 }
3310 {
3311 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3312 _("Opacity"),
3313 _("In color mode, act on objects' opacity"),
3314 NULL,
3315 Inkscape::ICON_SIZE_DECORATION );
3316 g_object_set( act, "short_label", _("O"), NULL );
3317 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3318 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3319 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3320 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3321 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3322 g_object_set_data( holder, "tweak_doo", act );
3323 }
3325 { /* Fidelity */
3326 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3327 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3328 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3329 _("Fidelity"), _("Fidelity:"),
3330 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3331 "tools.tweak", "fidelity", 50,
3332 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3333 1, 100, 1.0, 10.0,
3334 labels, values, G_N_ELEMENTS(labels),
3335 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3336 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3337 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3338 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3339 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3340 g_object_set_data( holder, "tweak_fidelity", eact );
3341 }
3344 /* Use Pressure button */
3345 {
3346 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3347 _("Pressure"),
3348 _("Use the pressure of the input device to alter the force of tweak action"),
3349 "use_pressure",
3350 Inkscape::ICON_SIZE_DECORATION );
3351 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3352 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3353 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3354 }
3356 }
3359 //########################
3360 //## Calligraphy ##
3361 //########################
3362 static void update_presets_list(GObject *dataKludge ){
3363 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3364 if (sel) {
3365 ege_select_one_action_set_active(sel, 0 );
3366 }
3367 }
3369 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3370 {
3371 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3372 update_presets_list(tbl);
3373 }
3375 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3376 {
3377 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3378 update_presets_list(tbl);
3379 }
3381 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3382 {
3383 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3384 update_presets_list(tbl);
3385 }
3387 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3388 {
3389 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3390 update_presets_list(tbl);
3391 }
3393 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3394 {
3395 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3396 update_presets_list(tbl);
3397 }
3399 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3400 {
3401 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3402 update_presets_list(tbl);
3403 }
3405 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3406 {
3407 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3408 update_presets_list(tbl);
3409 }
3411 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3412 {
3413 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3414 update_presets_list(tbl);
3415 }
3417 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3418 {
3419 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3420 update_presets_list(tbl);
3421 }
3423 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
3424 {
3425 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3426 update_presets_list(tbl);
3427 }
3429 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
3430 {
3431 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3432 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3433 update_presets_list(tbl);
3434 if (calligraphy_angle )
3435 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3436 }
3439 #define PROFILE_FLOAT_SIZE 7
3440 #define PROFILE_INT_SIZE 4
3441 struct ProfileFloatElement {
3442 char const *name;
3443 double def;
3444 double min;
3445 double max;
3446 };
3447 struct ProfileIntElement {
3448 char const *name;
3449 int def;
3450 int min;
3451 int max;
3452 };
3456 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3457 {"mass",0.02, 0.0, 1.0},
3458 {"wiggle",0.0, 0.0, 1.0},
3459 {"angle",30.0, -90.0, 90.0},
3460 {"thinning",0.1, -1.0, 1.0},
3461 {"tremor",0.0, 0.0, 1.0},
3462 {"flatness",0.9, 0.0, 1.0},
3463 {"cap_rounding",0.0, 0.0, 5.0}
3464 };
3465 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3466 {"width",15, 1, 100},
3467 {"usepressure",1,0,1},
3468 {"tracebackground",0,0,1},
3469 {"usetilt",1,0,1},
3470 };
3474 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3475 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3476 if (! desktop) return;
3478 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3479 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3480 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3482 unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3483 gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3484 gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);
3486 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3487 ProfileFloatElement const &pe = f_profile[i];
3488 double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3489 prefs_set_double_attribute(pref_path,pe.name,v);
3490 }
3491 for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3492 ProfileIntElement const &pe = i_profile[i];
3493 int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3494 prefs_set_int_attribute(pref_path,pe.name,v);
3495 }
3496 prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3498 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3499 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3500 GtkTreeIter iter;
3501 gtk_list_store_append( model, &iter );
3502 gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3504 free(profile_id);
3505 free(pref_path);
3507 ege_select_one_action_set_active(selector, new_index);
3508 }
3511 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3513 gint preset_index = ege_select_one_action_get_active( act );
3514 gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3516 if ( profile_name) {
3517 g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3518 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3519 ProfileFloatElement const &pe = f_profile[i];
3520 double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3521 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3522 if ( adj ) {
3523 gtk_adjustment_set_value(adj, v);
3524 }
3525 }
3526 for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3527 ProfileIntElement const &pe = i_profile[i];
3528 int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3529 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3530 if ( toggle ) {
3531 gtk_toggle_action_set_active(toggle, v);
3532 } else printf("No toggle");
3533 }
3534 free(profile_name);
3535 g_object_set_data(dataKludge, "profile_selector",act); //restore selector visibility
3536 }
3538 }
3541 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3542 {
3543 {
3544 EgeAdjustmentAction* calligraphy_angle = 0;
3546 {
3547 /* Width */
3548 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3549 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3550 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3551 _("Pen Width"), _("Width:"),
3552 _("The width of the calligraphic pen (relative to the visible canvas area)"),
3553 "tools.calligraphic", "width", 15,
3554 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3555 1, 100, 1.0, 10.0,
3556 labels, values, G_N_ELEMENTS(labels),
3557 sp_ddc_width_value_changed, 0.01, 0, 100 );
3558 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3559 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3560 }
3562 {
3563 /* Thinning */
3564 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3565 gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3566 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3567 _("Stroke Thinning"), _("Thinning:"),
3568 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3569 "tools.calligraphic", "thinning", 0.1,
3570 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3571 -1.0, 1.0, 0.01, 0.1,
3572 labels, values, G_N_ELEMENTS(labels),
3573 sp_ddc_velthin_value_changed, 0.01, 2);
3574 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3575 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3576 }
3578 {
3579 /* Angle */
3580 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3581 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3582 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3583 _("Pen Angle"), _("Angle:"),
3584 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3585 "tools.calligraphic", "angle", 30,
3586 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3587 -90.0, 90.0, 1.0, 10.0,
3588 labels, values, G_N_ELEMENTS(labels),
3589 sp_ddc_angle_value_changed, 1, 0 );
3590 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3591 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3592 g_object_set_data( holder, "angle", eact );
3593 calligraphy_angle = eact;
3594 }
3596 {
3597 /* Fixation */
3598 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3599 gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3600 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3601 _("Fixation"), _("Fixation:"),
3602 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3603 "tools.calligraphic", "flatness", 0.9,
3604 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3605 0.0, 1.0, 0.01, 0.1,
3606 labels, values, G_N_ELEMENTS(labels),
3607 sp_ddc_flatness_value_changed, 0.01, 2 );
3608 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3609 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3610 }
3612 {
3613 /* Cap Rounding */
3614 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3615 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3616 // TRANSLATORS: "cap" means "end" (both start and finish) here
3617 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3618 _("Cap rounding"), _("Caps:"),
3619 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3620 "tools.calligraphic", "cap_rounding", 0.0,
3621 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3622 0.0, 5.0, 0.01, 0.1,
3623 labels, values, G_N_ELEMENTS(labels),
3624 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3625 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3626 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3627 }
3629 {
3630 /* Tremor */
3631 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3632 gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3633 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3634 _("Stroke Tremor"), _("Tremor:"),
3635 _("Increase to make strokes rugged and trembling"),
3636 "tools.calligraphic", "tremor", 0.0,
3637 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3638 0.0, 1.0, 0.01, 0.1,
3639 labels, values, G_N_ELEMENTS(labels),
3640 sp_ddc_tremor_value_changed, 0.01, 2 );
3642 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3643 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3644 }
3646 {
3647 /* Wiggle */
3648 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3649 gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3650 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3651 _("Pen Wiggle"), _("Wiggle:"),
3652 _("Increase to make the pen waver and wiggle"),
3653 "tools.calligraphic", "wiggle", 0.0,
3654 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3655 0.0, 1.0, 0.01, 0.1,
3656 labels, values, G_N_ELEMENTS(labels),
3657 sp_ddc_wiggle_value_changed, 0.01, 2 );
3658 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3659 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3660 }
3662 {
3663 /* Mass */
3664 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3665 gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3666 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3667 _("Pen Mass"), _("Mass:"),
3668 _("Increase to make the pen drag behind, as if slowed by inertia"),
3669 "tools.calligraphic", "mass", 0.02,
3670 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3671 0.0, 1.0, 0.01, 0.1,
3672 labels, values, G_N_ELEMENTS(labels),
3673 sp_ddc_mass_value_changed, 0.01, 2 );
3674 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3675 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3676 }
3679 /* Trace Background button */
3680 {
3681 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3682 _("Trace Background"),
3683 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3684 "trace_background",
3685 Inkscape::ICON_SIZE_DECORATION );
3686 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3687 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
3688 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3689 g_object_set_data( holder, "tracebackground", act );
3690 }
3692 /* Use Pressure button */
3693 {
3694 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3695 _("Pressure"),
3696 _("Use the pressure of the input device to alter the width of the pen"),
3697 "use_pressure",
3698 Inkscape::ICON_SIZE_DECORATION );
3699 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3700 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
3701 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3702 g_object_set_data( holder, "usepressure", act );
3703 }
3705 /* Use Tilt button */
3706 {
3707 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3708 _("Tilt"),
3709 _("Use the tilt of the input device to alter the angle of the pen's nib"),
3710 "use_tilt",
3711 Inkscape::ICON_SIZE_DECORATION );
3712 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3713 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
3714 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3715 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3716 g_object_set_data( holder, "usetilt", act );
3717 }
3719 /*calligraphic profile */
3720 {
3721 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3722 gchar *pref_path;
3725 GtkTreeIter iter;
3726 gtk_list_store_append( model, &iter );
3727 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3729 //TODO: switch back to prefs API
3730 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3731 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3732 int ii=1;
3733 while (child_repr) {
3734 GtkTreeIter iter;
3735 char *preset_name = (char *) child_repr->attribute("name");
3736 gtk_list_store_append( model, &iter );
3737 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
3738 child_repr = sp_repr_next(child_repr);
3739 }
3741 pref_path = NULL;
3742 EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , ("Change calligraphic profile"), NULL, GTK_TREE_MODEL(model) );
3743 ege_select_one_action_set_appearance( act1, "compact" );
3744 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
3745 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3746 g_object_set_data( holder, "profile_selector", act1 );
3748 }
3750 /*Save or delete calligraphic profile */
3751 {
3752 GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
3753 _("Defaults"),
3754 _("Save current settings as new profile"),
3755 GTK_STOCK_SAVE );
3756 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
3759 gtk_action_group_add_action( mainActions, act );
3760 gtk_action_set_sensitive( act, TRUE );
3761 g_object_set_data( holder, "profile_save_delete", act );
3762 }
3763 }
3764 }
3767 //########################
3768 //## Circle / Arc ##
3769 //########################
3771 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3772 {
3773 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3774 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3776 if (v1 == 0 && v2 == 0) {
3777 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3778 gtk_action_set_sensitive( ocb, FALSE );
3779 gtk_action_set_sensitive( make_whole, FALSE );
3780 }
3781 } else {
3782 gtk_action_set_sensitive( ocb, TRUE );
3783 gtk_action_set_sensitive( make_whole, TRUE );
3784 }
3785 }
3787 static void
3788 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3789 {
3790 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3792 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3793 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3794 }
3796 // quit if run by the attr_changed listener
3797 if (g_object_get_data( tbl, "freeze" )) {
3798 return;
3799 }
3801 // in turn, prevent listener from responding
3802 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3804 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3806 bool modmade = false;
3807 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3808 items != NULL;
3809 items = items->next)
3810 {
3811 SPItem *item = SP_ITEM(items->data);
3813 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3815 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3816 SPArc *arc = SP_ARC(item);
3818 if (!strcmp(value_name, "start"))
3819 ge->start = (adj->value * M_PI)/ 180;
3820 else
3821 ge->end = (adj->value * M_PI)/ 180;
3823 sp_genericellipse_normalize(ge);
3824 ((SPObject *)arc)->updateRepr();
3825 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3827 modmade = true;
3828 }
3829 }
3831 g_free(namespaced_name);
3833 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3835 sp_arctb_sensitivize( tbl, adj->value, other->value );
3837 if (modmade) {
3838 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3839 _("Arc: Change start/end"));
3840 }
3842 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3843 }
3846 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
3847 {
3848 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
3849 }
3851 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3852 {
3853 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
3854 }
3856 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3857 {
3858 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3859 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3860 if ( ege_select_one_action_get_active( act ) != 0 ) {
3861 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3862 } else {
3863 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3864 }
3865 }
3867 // quit if run by the attr_changed listener
3868 if (g_object_get_data( tbl, "freeze" )) {
3869 return;
3870 }
3872 // in turn, prevent listener from responding
3873 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3875 bool modmade = false;
3877 if ( ege_select_one_action_get_active(act) != 0 ) {
3878 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3879 items != NULL;
3880 items = items->next)
3881 {
3882 if (SP_IS_ARC((SPItem *) items->data)) {
3883 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3884 repr->setAttribute("sodipodi:open", "true");
3885 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3886 modmade = true;
3887 }
3888 }
3889 } else {
3890 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3891 items != NULL;
3892 items = items->next)
3893 {
3894 if (SP_IS_ARC((SPItem *) items->data)) {
3895 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3896 repr->setAttribute("sodipodi:open", NULL);
3897 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3898 modmade = true;
3899 }
3900 }
3901 }
3903 if (modmade) {
3904 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3905 _("Arc: Change open/closed"));
3906 }
3908 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3909 }
3911 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3912 {
3913 GtkAdjustment *adj;
3914 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3915 gtk_adjustment_set_value(adj, 0.0);
3916 gtk_adjustment_value_changed(adj);
3918 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3919 gtk_adjustment_set_value(adj, 0.0);
3920 gtk_adjustment_value_changed(adj);
3922 spinbutton_defocus( GTK_OBJECT(obj) );
3923 }
3925 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3926 gchar const */*old_value*/, gchar const */*new_value*/,
3927 bool /*is_interactive*/, gpointer data)
3928 {
3929 GObject *tbl = G_OBJECT(data);
3931 // quit if run by the _changed callbacks
3932 if (g_object_get_data( tbl, "freeze" )) {
3933 return;
3934 }
3936 // in turn, prevent callbacks from responding
3937 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3939 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3940 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3942 GtkAdjustment *adj1,*adj2;
3943 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3944 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3945 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3946 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3948 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3950 char const *openstr = NULL;
3951 openstr = repr->attribute("sodipodi:open");
3952 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3954 if (openstr) {
3955 ege_select_one_action_set_active( ocb, 1 );
3956 } else {
3957 ege_select_one_action_set_active( ocb, 0 );
3958 }
3960 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3961 }
3963 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3964 NULL, /* child_added */
3965 NULL, /* child_removed */
3966 arc_tb_event_attr_changed,
3967 NULL, /* content_changed */
3968 NULL /* order_changed */
3969 };
3972 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3973 {
3974 int n_selected = 0;
3975 Inkscape::XML::Node *repr = NULL;
3977 purge_repr_listener( tbl, tbl );
3979 for (GSList const *items = selection->itemList();
3980 items != NULL;
3981 items = items->next)
3982 {
3983 if (SP_IS_ARC((SPItem *) items->data)) {
3984 n_selected++;
3985 repr = SP_OBJECT_REPR((SPItem *) items->data);
3986 }
3987 }
3989 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3991 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3992 if (n_selected == 0) {
3993 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3994 } else if (n_selected == 1) {
3995 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3996 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3998 if (repr) {
3999 g_object_set_data( tbl, "repr", repr );
4000 Inkscape::GC::anchor(repr);
4001 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4002 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4003 }
4004 } else {
4005 // FIXME: implement averaging of all parameters for multiple selected
4006 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4007 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4008 sp_arctb_sensitivize( tbl, 1, 0 );
4009 }
4010 }
4013 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4014 {
4015 EgeAdjustmentAction* eact = 0;
4018 {
4019 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4020 ege_output_action_set_use_markup( act, TRUE );
4021 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4022 g_object_set_data( holder, "mode_action", act );
4023 }
4025 /* Start */
4026 {
4027 eact = create_adjustment_action( "ArcStartAction",
4028 _("Start"), _("Start:"),
4029 _("The angle (in degrees) from the horizontal to the arc's start point"),
4030 "tools.shapes.arc", "start", 0.0,
4031 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4032 -360.0, 360.0, 1.0, 10.0,
4033 0, 0, 0,
4034 sp_arctb_start_value_changed);
4035 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4036 }
4038 /* End */
4039 {
4040 eact = create_adjustment_action( "ArcEndAction",
4041 _("End"), _("End:"),
4042 _("The angle (in degrees) from the horizontal to the arc's end point"),
4043 "tools.shapes.arc", "end", 0.0,
4044 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4045 -360.0, 360.0, 1.0, 10.0,
4046 0, 0, 0,
4047 sp_arctb_end_value_changed);
4048 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4049 }
4051 /* Segments / Pie checkbox */
4052 {
4053 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4055 GtkTreeIter iter;
4056 gtk_list_store_append( model, &iter );
4057 gtk_list_store_set( model, &iter,
4058 0, _("Closed arc"),
4059 1, _("Switch to segment (closed shape with two radii)"),
4060 2, "circle_closed_arc",
4061 -1 );
4063 gtk_list_store_append( model, &iter );
4064 gtk_list_store_set( model, &iter,
4065 0, _("Open Arc"),
4066 1, _("Switch to arc (unclosed shape)"),
4067 2, "circle_open_arc",
4068 -1 );
4070 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4071 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4072 g_object_set_data( holder, "open_action", act );
4074 ege_select_one_action_set_appearance( act, "full" );
4075 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4076 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4077 ege_select_one_action_set_icon_column( act, 2 );
4078 ege_select_one_action_set_tooltip_column( act, 1 );
4080 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4081 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4082 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4083 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4084 }
4086 /* Make Whole */
4087 {
4088 InkAction* inky = ink_action_new( "ArcResetAction",
4089 _("Make whole"),
4090 _("Make the shape a whole ellipse, not arc or segment"),
4091 "reset_circle",
4092 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4093 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4094 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4095 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4096 g_object_set_data( holder, "make_whole", inky );
4097 }
4099 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4100 // sensitivize make whole and open checkbox
4101 {
4102 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4103 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4104 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4105 }
4108 sigc::connection *connection = new sigc::connection(
4109 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4110 );
4111 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4112 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4113 }
4118 // toggle button callbacks and updaters
4120 //########################
4121 //## Dropper ##
4122 //########################
4124 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4125 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4126 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4127 if ( set_action ) {
4128 if ( gtk_toggle_action_get_active( act ) ) {
4129 gtk_action_set_sensitive( set_action, TRUE );
4130 } else {
4131 gtk_action_set_sensitive( set_action, FALSE );
4132 }
4133 }
4135 spinbutton_defocus(GTK_OBJECT(tbl));
4136 }
4138 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4139 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4140 spinbutton_defocus(GTK_OBJECT(tbl));
4141 }
4144 /**
4145 * Dropper auxiliary toolbar construction and setup.
4146 *
4147 * TODO: Would like to add swatch of current color.
4148 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4149 * can drag and drop places. Will provide a nice mixing palette.
4150 */
4151 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4152 {
4153 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4155 {
4156 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4157 ege_output_action_set_use_markup( act, TRUE );
4158 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4159 }
4161 {
4162 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4163 _("Pick opacity"),
4164 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4165 NULL,
4166 Inkscape::ICON_SIZE_DECORATION );
4167 g_object_set( act, "short_label", _("Pick"), NULL );
4168 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4169 g_object_set_data( holder, "pick_action", act );
4170 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4171 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4172 }
4174 {
4175 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4176 _("Assign opacity"),
4177 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4178 NULL,
4179 Inkscape::ICON_SIZE_DECORATION );
4180 g_object_set( act, "short_label", _("Assign"), NULL );
4181 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4182 g_object_set_data( holder, "set_action", act );
4183 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4184 // make sure it's disabled if we're not picking alpha
4185 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4186 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4187 }
4188 }
4191 //########################
4192 //## Text Toolbox ##
4193 //########################
4194 /*
4195 static void
4196 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4197 {
4198 //Call back for letter sizing spinbutton
4199 }
4201 static void
4202 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4203 {
4204 //Call back for line height spinbutton
4205 }
4207 static void
4208 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4209 {
4210 //Call back for horizontal kerning spinbutton
4211 }
4213 static void
4214 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4215 {
4216 //Call back for vertical kerning spinbutton
4217 }
4219 static void
4220 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4221 {
4222 //Call back for letter rotation spinbutton
4223 }*/
4225 namespace {
4227 bool popdown_visible = false;
4228 bool popdown_hasfocus = false;
4230 void
4231 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4232 {
4233 SPStyle *query =
4234 sp_style_new (SP_ACTIVE_DOCUMENT);
4236 int result_fontspec =
4237 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4239 int result_family =
4240 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4242 int result_style =
4243 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4245 int result_numbers =
4246 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4248 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4250 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4251 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4252 {
4253 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4255 if (repr)
4256 {
4257 sp_style_read_from_repr (query, repr);
4258 }
4259 else
4260 {
4261 return;
4262 }
4263 }
4265 if (query->text)
4266 {
4267 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4268 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4269 gtk_entry_set_text (GTK_ENTRY (entry), "");
4271 } else if (query->text->font_specification.value || query->text->font_family.value) {
4273 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4275 // Get the font that corresponds
4276 Glib::ustring familyName;
4278 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4279 if (font) {
4280 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4281 font->Unref();
4282 font = NULL;
4283 }
4285 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4287 Gtk::TreePath path;
4288 try {
4289 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4290 } catch (...) {
4291 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4292 return;
4293 }
4295 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4296 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4298 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4300 gtk_tree_selection_select_path (tselection, path.gobj());
4301 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4303 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4304 }
4306 //Size
4307 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4308 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4309 g_object_set_data (tbl, "size-block", gpointer(1));
4310 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4311 g_object_set_data (tbl, "size-block", gpointer(0));
4312 free (str);
4314 //Anchor
4315 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4316 {
4317 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4318 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4319 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4320 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4321 }
4322 else
4323 {
4324 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4325 {
4326 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4327 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4328 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4329 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4330 }
4331 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4332 {
4333 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4334 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4335 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4336 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4337 }
4338 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4339 {
4340 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4341 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4342 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4343 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4344 }
4345 }
4347 //Style
4348 {
4349 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4351 gboolean active = gtk_toggle_button_get_active (button);
4352 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4354 if (active != check)
4355 {
4356 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4357 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4358 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4359 }
4360 }
4362 {
4363 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4365 gboolean active = gtk_toggle_button_get_active (button);
4366 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4368 if (active != check)
4369 {
4370 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4371 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4372 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4373 }
4374 }
4376 //Orientation
4377 //locking both buttons, changing one affect all group (both)
4378 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4379 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4381 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4382 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4384 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4385 {
4386 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4387 }
4388 else
4389 {
4390 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4391 }
4392 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4393 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4394 }
4396 sp_style_unref(query);
4397 }
4399 void
4400 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4401 {
4402 sp_text_toolbox_selection_changed (selection, tbl);
4403 }
4405 void
4406 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4407 {
4408 sp_text_toolbox_selection_changed (NULL, tbl);
4409 }
4411 void
4412 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
4413 GObject *tbl)
4414 {
4415 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4416 GtkTreeModel *model = 0;
4417 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4418 GtkTreeIter iter;
4419 char *family = 0;
4421 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4422 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4424 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4425 return;
4426 }
4428 gtk_tree_model_get (model, &iter, 0, &family, -1);
4430 if (g_object_get_data (G_OBJECT (selection), "block"))
4431 {
4432 gtk_entry_set_text (GTK_ENTRY (entry), family);
4433 return;
4434 }
4436 gtk_entry_set_text (GTK_ENTRY (entry), family);
4438 SPStyle *query =
4439 sp_style_new (SP_ACTIVE_DOCUMENT);
4441 int result_fontspec =
4442 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4444 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4446 SPCSSAttr *css = sp_repr_css_attr_new ();
4449 // First try to get the font spec from the stored value
4450 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4452 if (fontSpec.empty()) {
4453 // Construct a new font specification if it does not yet exist
4454 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4455 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4456 fontFromStyle->Unref();
4457 }
4459 if (!fontSpec.empty()) {
4460 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4461 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4462 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4463 if (font) {
4464 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4466 // Set all the these just in case they were altered when finding the best
4467 // match for the new family and old style...
4469 gchar c[256];
4471 font->Family(c, 256);
4472 sp_repr_css_set_property (css, "font-family", c);
4474 font->Attribute( "weight", c, 256);
4475 sp_repr_css_set_property (css, "font-weight", c);
4477 font->Attribute("style", c, 256);
4478 sp_repr_css_set_property (css, "font-style", c);
4480 font->Attribute("stretch", c, 256);
4481 sp_repr_css_set_property (css, "font-stretch", c);
4483 font->Attribute("variant", c, 256);
4484 sp_repr_css_set_property (css, "font-variant", c);
4486 font->Unref();
4487 }
4488 }
4489 }
4491 // If querying returned nothing, set the default style of the tool (for new texts)
4492 if (result_fontspec == QUERY_STYLE_NOTHING)
4493 {
4494 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4495 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4496 }
4497 else
4498 {
4499 sp_desktop_set_style (desktop, css, true, true);
4500 }
4502 sp_style_unref(query);
4504 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4505 _("Text: Change font family"));
4506 sp_repr_css_attr_unref (css);
4507 free (family);
4508 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4510 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4511 }
4513 void
4514 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
4515 GObject *tbl)
4516 {
4517 const char *family = gtk_entry_get_text (entry);
4519 try {
4520 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4521 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4522 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4523 gtk_tree_selection_select_path (selection, path.gobj());
4524 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4525 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4526 } catch (...) {
4527 if (family && strlen (family))
4528 {
4529 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4530 }
4531 }
4532 }
4534 void
4535 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
4536 gpointer data)
4537 {
4538 if (g_object_get_data (G_OBJECT (button), "block")) return;
4539 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4540 int prop = GPOINTER_TO_INT(data);
4542 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4543 SPCSSAttr *css = sp_repr_css_attr_new ();
4545 switch (prop)
4546 {
4547 case 0:
4548 {
4549 sp_repr_css_set_property (css, "text-anchor", "start");
4550 sp_repr_css_set_property (css, "text-align", "start");
4551 break;
4552 }
4553 case 1:
4554 {
4555 sp_repr_css_set_property (css, "text-anchor", "middle");
4556 sp_repr_css_set_property (css, "text-align", "center");
4557 break;
4558 }
4560 case 2:
4561 {
4562 sp_repr_css_set_property (css, "text-anchor", "end");
4563 sp_repr_css_set_property (css, "text-align", "end");
4564 break;
4565 }
4567 case 3:
4568 {
4569 sp_repr_css_set_property (css, "text-anchor", "start");
4570 sp_repr_css_set_property (css, "text-align", "justify");
4571 break;
4572 }
4573 }
4575 SPStyle *query =
4576 sp_style_new (SP_ACTIVE_DOCUMENT);
4577 int result_numbers =
4578 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4580 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4581 if (result_numbers == QUERY_STYLE_NOTHING)
4582 {
4583 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4584 }
4586 sp_style_unref(query);
4588 sp_desktop_set_style (desktop, css, true, true);
4589 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4590 _("Text: Change alignment"));
4591 sp_repr_css_attr_unref (css);
4593 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4594 }
4596 void
4597 sp_text_toolbox_style_toggled (GtkToggleButton *button,
4598 gpointer data)
4599 {
4600 if (g_object_get_data (G_OBJECT (button), "block")) return;
4602 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4603 SPCSSAttr *css = sp_repr_css_attr_new ();
4604 int prop = GPOINTER_TO_INT(data);
4605 bool active = gtk_toggle_button_get_active (button);
4607 SPStyle *query =
4608 sp_style_new (SP_ACTIVE_DOCUMENT);
4610 int result_fontspec =
4611 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4613 int result_family =
4614 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4616 int result_style =
4617 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4619 int result_numbers =
4620 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4622 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4623 Glib::ustring newFontSpec = "";
4625 if (fontSpec.empty()) {
4626 // Construct a new font specification if it does not yet exist
4627 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4628 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4629 fontFromStyle->Unref();
4630 }
4632 switch (prop)
4633 {
4634 case 0:
4635 {
4636 if (!fontSpec.empty()) {
4637 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4638 }
4639 if (fontSpec != newFontSpec) {
4640 // Don't even set the bold if the font didn't exist on the system
4641 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4642 }
4643 break;
4644 }
4646 case 1:
4647 {
4648 if (!fontSpec.empty()) {
4649 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4650 }
4651 if (fontSpec != newFontSpec) {
4652 // Don't even set the italic if the font didn't exist on the system
4653 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4654 }
4655 break;
4656 }
4657 }
4659 if (!newFontSpec.empty()) {
4660 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4661 }
4663 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4664 if (result_fontspec == QUERY_STYLE_NOTHING)
4665 {
4666 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4667 }
4669 sp_style_unref(query);
4671 sp_desktop_set_style (desktop, css, true, true);
4672 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4673 _("Text: Change font style"));
4674 sp_repr_css_attr_unref (css);
4676 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4677 }
4679 void
4680 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
4681 gpointer data)
4682 {
4683 if (g_object_get_data (G_OBJECT (button), "block")) {
4684 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4685 return;
4686 }
4688 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4689 SPCSSAttr *css = sp_repr_css_attr_new ();
4690 int prop = GPOINTER_TO_INT(data);
4692 switch (prop)
4693 {
4694 case 0:
4695 {
4696 sp_repr_css_set_property (css, "writing-mode", "lr");
4697 break;
4698 }
4700 case 1:
4701 {
4702 sp_repr_css_set_property (css, "writing-mode", "tb");
4703 break;
4704 }
4705 }
4707 SPStyle *query =
4708 sp_style_new (SP_ACTIVE_DOCUMENT);
4709 int result_numbers =
4710 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4712 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4713 if (result_numbers == QUERY_STYLE_NOTHING)
4714 {
4715 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4716 }
4718 sp_desktop_set_style (desktop, css, true, true);
4719 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4720 _("Text: Change orientation"));
4721 sp_repr_css_attr_unref (css);
4723 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4724 }
4726 gboolean
4727 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4728 {
4729 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4730 if (!desktop) return FALSE;
4732 switch (get_group0_keyval (event)) {
4733 case GDK_Escape: // defocus
4734 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4735 sp_text_toolbox_selection_changed (NULL, tbl); // update
4736 return TRUE; // I consumed the event
4737 break;
4738 }
4739 return FALSE;
4740 }
4742 gboolean
4743 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4744 {
4745 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4746 if (!desktop) return FALSE;
4748 switch (get_group0_keyval (event)) {
4749 case GDK_KP_Enter:
4750 case GDK_Return:
4751 case GDK_Escape: // defocus
4752 gtk_widget_hide (w);
4753 popdown_visible = false;
4754 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4755 return TRUE; // I consumed the event
4756 break;
4757 case GDK_w:
4758 case GDK_W:
4759 if (event->state & GDK_CONTROL_MASK) {
4760 gtk_widget_hide (w);
4761 popdown_visible = false;
4762 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4763 return TRUE; // I consumed the event
4764 }
4765 break;
4766 }
4767 return FALSE;
4768 }
4771 void
4772 sp_text_toolbox_size_changed (GtkComboBox *cbox,
4773 GObject *tbl)
4774 {
4775 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4777 if (g_object_get_data (tbl, "size-block")) return;
4779 // If this is not from selecting a size in the list (in which case get_active will give the
4780 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4781 // process this event. This fixes GTK's stupid insistence on sending an activate change every
4782 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4783 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4784 return;
4786 gchar *endptr;
4787 gdouble value = -1;
4788 char *text = gtk_combo_box_get_active_text (cbox);
4789 if (text) {
4790 value = g_strtod (text, &endptr);
4791 if (endptr == text) // conversion failed, non-numeric input
4792 value = -1;
4793 free (text);
4794 }
4795 if (value <= 0) {
4796 return; // could not parse value
4797 }
4799 SPCSSAttr *css = sp_repr_css_attr_new ();
4800 Inkscape::CSSOStringStream osfs;
4801 osfs << value;
4802 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4804 SPStyle *query =
4805 sp_style_new (SP_ACTIVE_DOCUMENT);
4806 int result_numbers =
4807 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4809 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4810 if (result_numbers == QUERY_STYLE_NOTHING)
4811 {
4812 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4813 }
4815 sp_style_unref(query);
4817 sp_desktop_set_style (desktop, css, true, true);
4818 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4819 _("Text: Change font size"));
4820 sp_repr_css_attr_unref (css);
4822 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4823 }
4825 gboolean
4826 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
4827 {
4828 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4829 if (!desktop) return FALSE;
4831 if (!g_object_get_data (tbl, "esc-pressed")) {
4832 g_object_set_data (tbl, "enter-pressed", gpointer(1));
4833 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4834 sp_text_toolbox_size_changed (cbox, tbl);
4835 g_object_set_data (tbl, "enter-pressed", gpointer(0));
4836 }
4837 return FALSE; // I consumed the event
4838 }
4841 gboolean
4842 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4843 {
4844 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4845 if (!desktop) return FALSE;
4847 switch (get_group0_keyval (event)) {
4848 case GDK_Escape: // defocus
4849 g_object_set_data (tbl, "esc-pressed", gpointer(1));
4850 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4851 g_object_set_data (tbl, "esc-pressed", gpointer(0));
4852 return TRUE; // I consumed the event
4853 break;
4854 case GDK_Return: // defocus
4855 case GDK_KP_Enter:
4856 g_object_set_data (tbl, "enter-pressed", gpointer(1));
4857 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4858 sp_text_toolbox_size_changed (cbox, tbl);
4859 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4860 g_object_set_data (tbl, "enter-pressed", gpointer(0));
4861 return TRUE; // I consumed the event
4862 break;
4863 }
4864 return FALSE;
4865 }
4867 void
4868 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
4869 GObject *tbl)
4870 {
4871 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4872 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4873 int x, y;
4875 if (!popdown_visible)
4876 {
4877 gdk_window_get_origin (widget->window, &x, &y);
4878 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4879 gtk_widget_show_all (popdown);
4880 //sp_transientize (popdown);
4882 gdk_pointer_grab (widget->window, TRUE,
4883 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4884 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4885 GDK_POINTER_MOTION_MASK),
4886 NULL, NULL, GDK_CURRENT_TIME);
4888 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4890 popdown_visible = true;
4891 }
4892 else
4893 {
4894 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4895 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4896 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4897 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4898 gtk_widget_hide (popdown);
4899 popdown_visible = false;
4900 }
4901 }
4903 gboolean
4904 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
4905 GdkEventFocus */*event*/,
4906 GObject */*tbl*/)
4907 {
4908 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4909 return FALSE;
4910 }
4912 gboolean
4913 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
4914 GdkEventFocus */*event*/,
4915 GObject */*tbl*/)
4916 {
4917 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4919 if (popdown_hasfocus) {
4920 gtk_widget_hide (popdown);
4921 popdown_hasfocus = false;
4922 popdown_visible = false;
4923 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4924 return TRUE;
4925 }
4926 return FALSE;
4927 }
4929 gboolean
4930 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
4931 GdkEventFocus */*event*/,
4932 GObject */*tbl*/)
4933 {
4934 popdown_hasfocus = true;
4935 return TRUE;
4936 }
4939 void
4940 cell_data_func (GtkTreeViewColumn */*column*/,
4941 GtkCellRenderer *cell,
4942 GtkTreeModel *tree_model,
4943 GtkTreeIter *iter,
4944 gpointer /*data*/)
4945 {
4946 char *family,
4947 *family_escaped,
4948 *sample_escaped;
4950 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4952 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4954 family_escaped = g_markup_escape_text (family, -1);
4955 sample_escaped = g_markup_escape_text (sample, -1);
4957 std::stringstream markup;
4958 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4959 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4961 free (family);
4962 free (family_escaped);
4963 free (sample_escaped);
4964 }
4966 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4967 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4968 if (completion) {
4969 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4970 g_object_unref (completion);
4971 }
4972 }
4974 GtkWidget*
4975 sp_text_toolbox_new (SPDesktop *desktop)
4976 {
4977 GtkWidget *tbl = gtk_hbox_new (FALSE, 0);
4979 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4980 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4982 GtkTooltips *tt = gtk_tooltips_new();
4983 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4985 ////////////Family
4986 //Window
4987 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4988 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4990 //Entry
4991 GtkWidget *entry = gtk_entry_new ();
4992 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4993 GtkEntryCompletion *completion = gtk_entry_completion_new ();
4994 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4995 gtk_entry_completion_set_text_column (completion, 0);
4996 gtk_entry_completion_set_minimum_key_length (completion, 1);
4997 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4998 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4999 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5000 aux_toolbox_space (tbl, 1);
5001 gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
5002 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5004 //Button
5005 GtkWidget *button = gtk_button_new ();
5006 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5007 gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, 0);
5009 //Popdown
5010 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
5011 GtkWidget *treeview = gtk_tree_view_new ();
5013 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5014 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
5015 gtk_tree_view_column_pack_start (column, cell, FALSE);
5016 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5017 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5018 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5020 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5021 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5022 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5024 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5026 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5027 gtk_container_add (GTK_CONTAINER (sw), treeview);
5029 gtk_container_add (GTK_CONTAINER (window), sw);
5030 gtk_widget_set_size_request (window, 300, 450);
5032 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5033 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5034 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5036 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5038 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5039 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5040 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5042 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5043 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5045 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5046 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5047 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5048 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5049 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5051 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
5052 aux_toolbox_space (tbl, 1);
5053 GtkWidget *box = gtk_event_box_new ();
5054 gtk_container_add (GTK_CONTAINER (box), image);
5055 gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
5056 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5057 GtkTooltips *tooltips = gtk_tooltips_new ();
5058 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5059 gtk_widget_hide (GTK_WIDGET (box));
5060 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5062 ////////////Size
5063 const char *sizes[] = {
5064 "4", "6", "8", "9", "10", "11", "12", "13", "14",
5065 "16", "18", "20", "22", "24", "28",
5066 "32", "36", "40", "48", "56", "64", "72", "144"
5067 };
5069 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5070 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5071 gtk_widget_set_size_request (cbox, 80, -1);
5072 aux_toolbox_space (tbl, 1);
5073 gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
5074 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5075 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5076 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5077 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5079 //spacer
5080 aux_toolbox_space (tbl, 4);
5081 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5083 ////////////Text anchor
5084 GtkWidget *group = gtk_radio_button_new (NULL);
5085 GtkWidget *row = gtk_hbox_new (FALSE, 4);
5086 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5088 // left
5089 GtkWidget *rbutton = group;
5090 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5091 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
5092 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5094 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5095 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
5096 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5097 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5099 // center
5100 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5101 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5102 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
5103 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5105 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5106 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
5107 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5108 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5110 // right
5111 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5112 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5113 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
5114 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5116 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5117 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5118 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5119 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5121 // fill
5122 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5123 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5124 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
5125 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5127 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5128 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
5129 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5130 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5132 aux_toolbox_space (tbl, 1);
5133 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5135 //spacer
5136 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5138 ////////////Text style
5139 row = gtk_hbox_new (FALSE, 4);
5141 // bold
5142 rbutton = gtk_toggle_button_new ();
5143 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5144 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
5145 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5146 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5148 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5149 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
5150 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5152 // italic
5153 rbutton = gtk_toggle_button_new ();
5154 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5155 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
5156 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5157 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5159 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5160 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
5161 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5163 aux_toolbox_space (tbl, 1);
5164 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5166 //spacer
5167 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5169 ////////////Text orientation
5170 group = gtk_radio_button_new (NULL);
5171 row = gtk_hbox_new (FALSE, 4);
5172 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5174 // horizontal
5175 rbutton = group;
5176 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5177 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
5178 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5179 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5181 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5182 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5183 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5185 // vertical
5186 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5187 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5188 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
5189 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5190 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5192 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5193 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
5194 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5195 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5198 //watch selection
5199 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5201 sigc::connection *c_selection_changed =
5202 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5203 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5204 pool->add_connection ("selection-changed", c_selection_changed);
5206 sigc::connection *c_selection_modified =
5207 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5208 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5209 pool->add_connection ("selection-modified", c_selection_modified);
5211 sigc::connection *c_subselection_changed =
5212 new sigc::connection (desktop->connectToolSubselectionChanged
5213 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5214 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5216 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5219 gtk_widget_show_all (tbl);
5220 return tbl;
5222 } // end of sp_text_toolbox_new()
5224 }//<unnamed> namespace
5227 //#########################
5228 //## Connector ##
5229 //#########################
5231 static void sp_connector_path_set_avoid(void)
5232 {
5233 cc_selection_set_avoid(true);
5234 }
5237 static void sp_connector_path_set_ignore(void)
5238 {
5239 cc_selection_set_avoid(false);
5240 }
5244 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5245 {
5246 // quit if run by the _changed callbacks
5247 if (g_object_get_data( tbl, "freeze" )) {
5248 return;
5249 }
5251 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5252 SPDocument *doc = sp_desktop_document(desktop);
5254 if (!sp_document_get_undo_sensitive(doc))
5255 {
5256 return;
5257 }
5259 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5261 if ( repr->attribute("inkscape:connector-spacing") ) {
5262 gdouble priorValue = gtk_adjustment_get_value(adj);
5263 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5264 if ( priorValue == gtk_adjustment_get_value(adj) ) {
5265 return;
5266 }
5267 } else if ( adj->value == defaultConnSpacing ) {
5268 return;
5269 }
5271 // in turn, prevent callbacks from responding
5272 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5274 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5275 SP_OBJECT(desktop->namedview)->updateRepr();
5277 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5278 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5279 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5280 NR::Matrix m = NR::identity();
5281 avoid_item_move(&m, item);
5282 }
5284 if (items) {
5285 g_slist_free(items);
5286 }
5288 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5289 _("Change connector spacing"));
5291 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5293 spinbutton_defocus(GTK_OBJECT(tbl));
5294 }
5296 static void sp_connector_graph_layout(void)
5297 {
5298 if (!SP_ACTIVE_DESKTOP) return;
5300 // hack for clones, see comment in align-and-distribute.cpp
5301 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5302 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5304 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5306 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5308 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5309 }
5311 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5312 {
5313 if ( gtk_toggle_action_get_active( act ) ) {
5314 prefs_set_string_attribute("tools.connector", "directedlayout",
5315 "true");
5316 } else {
5317 prefs_set_string_attribute("tools.connector", "directedlayout",
5318 "false");
5319 }
5320 }
5322 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5323 {
5324 if ( gtk_toggle_action_get_active( act ) ) {
5325 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5326 "true");
5327 } else {
5328 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5329 "false");
5330 }
5331 }
5334 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5335 {
5336 prefs_set_double_attribute("tools.connector", "length", adj->value);
5337 spinbutton_defocus(GTK_OBJECT(tbl));
5338 }
5340 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5341 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5342 bool /*is_interactive*/, gpointer data)
5343 {
5344 GtkWidget *tbl = GTK_WIDGET(data);
5346 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5347 return;
5348 }
5349 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5350 return;
5351 }
5353 GtkAdjustment *adj = (GtkAdjustment*)
5354 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5355 gdouble spacing = defaultConnSpacing;
5356 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5358 gtk_adjustment_set_value(adj, spacing);
5359 }
5362 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5363 NULL, /* child_added */
5364 NULL, /* child_removed */
5365 connector_tb_event_attr_changed,
5366 NULL, /* content_changed */
5367 NULL /* order_changed */
5368 };
5371 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5372 {
5373 {
5374 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5375 _("Avoid"),
5376 _("Make connectors avoid selected objects"),
5377 "connector_avoid",
5378 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5379 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5380 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5381 }
5383 {
5384 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5385 _("Ignore"),
5386 _("Make connectors ignore selected objects"),
5387 "connector_ignore",
5388 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5389 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5390 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5391 }
5393 EgeAdjustmentAction* eact = 0;
5395 // Spacing spinbox
5396 eact = create_adjustment_action( "ConnectorSpacingAction",
5397 _("Connector Spacing"), _("Spacing:"),
5398 _("The amount of space left around objects by auto-routing connectors"),
5399 "tools.connector", "spacing", defaultConnSpacing,
5400 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5401 0, 100, 1.0, 10.0,
5402 0, 0, 0,
5403 connector_spacing_changed, 1, 0 );
5404 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5406 // Graph (connector network) layout
5407 {
5408 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5409 _("Graph"),
5410 _("Nicely arrange selected connector network"),
5411 "graph_layout",
5412 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5413 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5414 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5415 }
5417 // Default connector length spinbox
5418 eact = create_adjustment_action( "ConnectorLengthAction",
5419 _("Connector Length"), _("Length:"),
5420 _("Ideal length for connectors when layout is applied"),
5421 "tools.connector", "length", 100,
5422 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5423 10, 1000, 10.0, 100.0,
5424 0, 0, 0,
5425 connector_length_changed, 1, 0 );
5426 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5429 // Directed edges toggle button
5430 {
5431 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5432 _("Downwards"),
5433 _("Make connectors with end-markers (arrows) point downwards"),
5434 "directed_graph",
5435 Inkscape::ICON_SIZE_DECORATION );
5436 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5438 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5439 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5440 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5442 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5443 }
5445 // Avoid overlaps toggle button
5446 {
5447 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5448 _("Remove overlaps"),
5449 _("Do not allow overlapping shapes"),
5450 "remove_overlaps",
5451 Inkscape::ICON_SIZE_DECORATION );
5452 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5454 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5455 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5456 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5458 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5459 }
5461 // Code to watch for changes to the connector-spacing attribute in
5462 // the XML.
5463 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5464 g_assert(repr != NULL);
5466 purge_repr_listener( holder, holder );
5468 if (repr) {
5469 g_object_set_data( holder, "repr", repr );
5470 Inkscape::GC::anchor(repr);
5471 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5472 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5473 }
5474 } // end of sp_connector_toolbox_prep()
5477 //#########################
5478 //## Paintbucket ##
5479 //#########################
5481 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5482 {
5483 gint channels = ege_select_one_action_get_active( act );
5484 flood_channels_set_channels( channels );
5485 }
5487 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5488 {
5489 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5490 }
5492 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5493 {
5494 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5495 }
5497 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5498 {
5499 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5500 SPUnit const *unit = tracker->getActiveUnit();
5502 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5504 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5505 }
5507 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5508 {
5509 // FIXME: make defaults settable via Inkscape Options
5510 struct KeyValue {
5511 char const *key;
5512 double value;
5513 } const key_values[] = {
5514 {"threshold", 15},
5515 {"offset", 0.0}
5516 };
5518 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5519 KeyValue const &kv = key_values[i];
5520 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5521 if ( adj ) {
5522 gtk_adjustment_set_value(adj, kv.value);
5523 }
5524 }
5526 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5527 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5528 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5529 ege_select_one_action_set_active( autogap_action, 0 );
5530 }
5532 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5533 {
5534 EgeAdjustmentAction* eact = 0;
5536 {
5537 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5539 GList* items = 0;
5540 gint count = 0;
5541 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5542 {
5543 GtkTreeIter iter;
5544 gtk_list_store_append( model, &iter );
5545 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5546 count++;
5547 }
5548 g_list_free( items );
5549 items = 0;
5550 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5551 g_object_set( act1, "short_label", _("Fill by:"), NULL );
5552 ege_select_one_action_set_appearance( act1, "compact" );
5553 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5554 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5555 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5556 g_object_set_data( holder, "channels_action", act1 );
5557 }
5559 // Spacing spinbox
5560 {
5561 eact = create_adjustment_action(
5562 "ThresholdAction",
5563 _("Fill Threshold"), _("Threshold:"),
5564 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5565 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5566 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5567 0, 0, 0,
5568 paintbucket_threshold_changed, 1, 0 );
5570 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5571 }
5573 // Create the units menu.
5574 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5575 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5576 if (stored_unit)
5577 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5578 g_object_set_data( holder, "tracker", tracker );
5579 {
5580 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5581 gtk_action_group_add_action( mainActions, act );
5582 }
5584 // Offset spinbox
5585 {
5586 eact = create_adjustment_action(
5587 "OffsetAction",
5588 _("Grow/shrink by"), _("Grow/shrink by:"),
5589 _("The amount to grow (positive) or shrink (negative) the created fill path"),
5590 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5591 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5592 0, 0, 0,
5593 paintbucket_offset_changed, 1, 2);
5594 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5596 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5597 }
5599 /* Auto Gap */
5600 {
5601 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5603 GList* items = 0;
5604 gint count = 0;
5605 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5606 {
5607 GtkTreeIter iter;
5608 gtk_list_store_append( model, &iter );
5609 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5610 count++;
5611 }
5612 g_list_free( items );
5613 items = 0;
5614 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5615 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5616 ege_select_one_action_set_appearance( act2, "compact" );
5617 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5618 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5619 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5620 g_object_set_data( holder, "autogap_action", act2 );
5621 }
5623 /* Reset */
5624 {
5625 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5626 _("Defaults"),
5627 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5628 GTK_STOCK_CLEAR );
5629 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5630 gtk_action_group_add_action( mainActions, act );
5631 gtk_action_set_sensitive( act, TRUE );
5632 }
5634 }
5636 /*
5637 Local Variables:
5638 mode:c++
5639 c-file-style:"stroustrup"
5640 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5641 indent-tabs-mode:nil
5642 fill-column:99
5643 End:
5644 */
5645 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :