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", N_("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", N_("Style of new stars")},
178 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
179 SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", N_("Style of new rectangles")},
180 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
181 SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", N_("Style of new 3D boxes")},
182 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
183 SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", N_("Style of new ellipses")},
184 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
185 SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral", N_("Style of new spirals")},
186 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
187 SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("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", N_("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", N_("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", N_("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_preserving_shape();
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_LPE_ITEM(item)) {
997 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
998 gtk_action_set_sensitive(w, TRUE);
999 } else {
1000 gtk_action_set_sensitive(w, FALSE);
1001 }
1002 } else {
1003 gtk_action_set_sensitive(w, FALSE);
1004 }
1005 }
1007 {
1008 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1009 SPItem *item = selection->singleItem();
1010 if (item && item->clip_ref && item->clip_ref->getObject()) {
1011 gtk_action_set_sensitive(w, TRUE);
1012 } else {
1013 gtk_action_set_sensitive(w, FALSE);
1014 }
1015 }
1017 {
1018 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1019 SPItem *item = selection->singleItem();
1020 if (item && item->mask_ref && item->mask_ref->getObject()) {
1021 gtk_action_set_sensitive(w, TRUE);
1022 } else {
1023 gtk_action_set_sensitive(w, FALSE);
1024 }
1025 }
1026 }
1028 void
1029 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1030 {
1031 sp_node_toolbox_sel_changed (selection, tbl);
1032 }
1036 //################################
1037 //## Node Editing Toolbox ##
1038 //################################
1040 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1041 {
1042 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1043 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1044 g_object_set_data( holder, "tracker", tracker );
1046 {
1047 InkAction* inky = ink_action_new( "NodeInsertAction",
1048 _("Insert node"),
1049 _("Insert new nodes into selected segments"),
1050 "node_insert",
1051 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1052 g_object_set( inky, "short_label", _("Insert"), NULL );
1053 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1054 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1055 }
1057 {
1058 InkAction* inky = ink_action_new( "NodeDeleteAction",
1059 _("Delete node"),
1060 _("Delete selected nodes"),
1061 "node_delete",
1062 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1063 g_object_set( inky, "short_label", _("Delete"), NULL );
1064 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1065 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1066 }
1068 {
1069 InkAction* inky = ink_action_new( "NodeJoinAction",
1070 _("Join endnodes"),
1071 _("Join selected endnodes"),
1072 "node_join",
1073 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1074 g_object_set( inky, "short_label", _("Join"), NULL );
1075 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1076 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1077 }
1079 {
1080 InkAction* inky = ink_action_new( "NodeBreakAction",
1081 _("Break nodes"),
1082 _("Break path at selected nodes"),
1083 "node_break",
1084 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1085 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1086 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1087 }
1090 {
1091 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1092 _("Join with segment"),
1093 _("Join selected endnodes with a new segment"),
1094 "node_join_segment",
1095 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1096 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1097 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1098 }
1100 {
1101 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1102 _("Delete segment"),
1103 _("Delete segment between two non-endpoint nodes"),
1104 "node_delete_segment",
1105 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1106 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1107 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1108 }
1110 {
1111 InkAction* inky = ink_action_new( "NodeCuspAction",
1112 _("Node Cusp"),
1113 _("Make selected nodes corner"),
1114 "node_cusp",
1115 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1116 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1117 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1118 }
1120 {
1121 InkAction* inky = ink_action_new( "NodeSmoothAction",
1122 _("Node Smooth"),
1123 _("Make selected nodes smooth"),
1124 "node_smooth",
1125 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1126 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1127 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1128 }
1130 {
1131 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1132 _("Node Symmetric"),
1133 _("Make selected nodes symmetric"),
1134 "node_symmetric",
1135 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1136 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1137 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1138 }
1140 {
1141 InkAction* inky = ink_action_new( "NodeLineAction",
1142 _("Node Line"),
1143 _("Make selected segments lines"),
1144 "node_line",
1145 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1146 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1147 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1148 }
1150 {
1151 InkAction* inky = ink_action_new( "NodeCurveAction",
1152 _("Node Curve"),
1153 _("Make selected segments curves"),
1154 "node_curve",
1155 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1156 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1157 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1158 }
1160 {
1161 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1162 _("Show Handles"),
1163 _("Show the Bezier handles of selected nodes"),
1164 "nodes_show_handles",
1165 Inkscape::ICON_SIZE_DECORATION );
1166 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1167 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1168 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1169 }
1171 {
1172 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1173 _("Show Outline"),
1174 _("Show the outline of the path"),
1175 "nodes_show_helperpath",
1176 Inkscape::ICON_SIZE_DECORATION );
1177 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1178 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1179 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1180 }
1182 {
1183 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1184 _("Next path effect parameter"),
1185 _("Show next path effect parameter for editing"),
1186 "edit_next_parameter",
1187 Inkscape::ICON_SIZE_DECORATION );
1188 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1189 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1190 g_object_set_data( holder, "nodes_lpeedit", inky);
1191 }
1193 {
1194 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1195 _("Edit clipping path"),
1196 _("Edit the clipping path of the object"),
1197 "nodeedit-clippath",
1198 Inkscape::ICON_SIZE_DECORATION );
1199 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1200 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1201 g_object_set_data( holder, "nodes_clippathedit", inky);
1202 }
1204 {
1205 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1206 _("Edit mask path"),
1207 _("Edit the mask of the object"),
1208 "nodeedit-mask",
1209 Inkscape::ICON_SIZE_DECORATION );
1210 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1211 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1212 g_object_set_data( holder, "nodes_maskedit", inky);
1213 }
1215 /* X coord of selected node(s) */
1216 {
1217 EgeAdjustmentAction* eact = 0;
1218 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1219 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1220 eact = create_adjustment_action( "NodeXAction",
1221 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1222 "tools.nodes", "Xcoord", 0,
1223 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1224 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1225 labels, values, G_N_ELEMENTS(labels),
1226 sp_node_path_x_value_changed );
1227 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1228 g_object_set_data( holder, "nodes_x_action", eact );
1229 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1230 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1231 }
1233 /* Y coord of selected node(s) */
1234 {
1235 EgeAdjustmentAction* eact = 0;
1236 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1237 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1238 eact = create_adjustment_action( "NodeYAction",
1239 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1240 "tools.nodes", "Ycoord", 0,
1241 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1242 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1243 labels, values, G_N_ELEMENTS(labels),
1244 sp_node_path_y_value_changed );
1245 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1246 g_object_set_data( holder, "nodes_y_action", eact );
1247 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1248 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1249 }
1251 // add the units menu
1252 {
1253 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1254 gtk_action_group_add_action( mainActions, act );
1255 }
1258 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1260 //watch selection
1261 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1263 sigc::connection *c_selection_changed =
1264 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1265 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1266 pool->add_connection ("selection-changed", c_selection_changed);
1268 sigc::connection *c_selection_modified =
1269 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1270 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1271 pool->add_connection ("selection-modified", c_selection_modified);
1273 sigc::connection *c_subselection_changed =
1274 new sigc::connection (desktop->connectToolSubselectionChanged
1275 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1276 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1278 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1280 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1281 } // end of sp_node_toolbox_prep()
1284 //########################
1285 //## Zoom Toolbox ##
1286 //########################
1288 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1289 {
1290 // no custom GtkAction setup needed
1291 } // end of sp_zoom_toolbox_prep()
1293 void
1294 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1295 {
1296 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")));
1297 }
1300 void
1301 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1302 {
1303 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")));
1304 }
1306 void
1307 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1308 {
1309 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")));
1310 }
1312 static void
1313 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1314 {
1315 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1316 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1318 if (old_desktop) {
1319 GList *children, *iter;
1321 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1322 for ( iter = children ; iter ; iter = iter->next ) {
1323 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1324 }
1325 g_list_free(children);
1326 }
1328 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1330 if (desktop) {
1331 gtk_widget_set_sensitive(toolbox, TRUE);
1332 setup_func(toolbox, desktop);
1333 update_func(desktop, desktop->event_context, toolbox);
1334 *conn = desktop->connectEventContextChanged
1335 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1336 } else {
1337 gtk_widget_set_sensitive(toolbox, FALSE);
1338 }
1340 } // end of toolbox_set_desktop()
1343 static void
1344 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1345 {
1346 GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1347 gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1348 if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1349 // "toolbox.tools" was not set. Fallback to older value
1350 shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1352 // Copy the setting forwards
1353 prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1354 }
1355 Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1357 for (int i = 0 ; tools[i].type_name ; i++ ) {
1358 GtkWidget *button =
1359 sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1360 SP_BUTTON_TYPE_TOGGLE,
1361 Inkscape::Verb::get(tools[i].verb),
1362 Inkscape::Verb::get(tools[i].doubleclick_verb),
1363 desktop,
1364 tooltips );
1366 g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1367 (gpointer)button );
1368 }
1369 }
1372 static void
1373 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1374 {
1375 gchar const *const tname = ( eventcontext
1376 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1377 : NULL );
1378 for (int i = 0 ; tools[i].type_name ; i++ ) {
1379 SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1380 sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1381 }
1382 }
1384 static void
1385 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1386 {
1387 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1388 GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1389 GtkUIManager* mgr = gtk_ui_manager_new();
1390 GError* errVal = 0;
1391 gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1392 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1394 std::map<std::string, GtkWidget*> dataHolders;
1396 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1397 if ( aux_toolboxes[i].prep_func ) {
1398 // converted to GtkActions and UIManager
1400 GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1401 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1402 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1403 dataHolders[aux_toolboxes[i].type_name] = kludge;
1404 aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1405 } else {
1407 GtkWidget *sub_toolbox = 0;
1408 if (aux_toolboxes[i].create_func == NULL)
1409 sub_toolbox = sp_empty_toolbox_new(desktop);
1410 else {
1411 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1412 }
1414 gtk_size_group_add_widget( grouper, sub_toolbox );
1416 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1417 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1419 }
1420 }
1422 // Second pass to create toolbars *after* all GtkActions are created
1423 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1424 if ( aux_toolboxes[i].prep_func ) {
1425 // converted to GtkActions and UIManager
1427 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1429 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1430 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1432 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1433 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1434 g_free( tmp );
1435 tmp = 0;
1437 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1438 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1439 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1440 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1441 }
1442 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1445 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1447 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1448 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1449 swatch->setDesktop( desktop );
1450 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1451 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1452 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1453 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 );
1454 }
1456 gtk_widget_show_all( holder );
1457 sp_set_font_size_smaller( holder );
1459 gtk_size_group_add_widget( grouper, holder );
1461 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1462 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1463 }
1464 }
1466 g_object_unref( G_OBJECT(grouper) );
1467 }
1469 static void
1470 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1471 {
1472 gchar const *tname = ( eventcontext
1473 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1474 : NULL );
1475 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1476 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1477 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1478 gtk_widget_show_all(sub_toolbox);
1479 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1480 } else {
1481 gtk_widget_hide(sub_toolbox);
1482 }
1483 }
1484 }
1486 static void
1487 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1488 {
1489 gchar const * descr =
1490 "<ui>"
1491 " <toolbar name='CommandsToolbar'>"
1492 " <toolitem action='FileNew' />"
1493 " <toolitem action='FileOpen' />"
1494 " <toolitem action='FileSave' />"
1495 " <toolitem action='FilePrint' />"
1496 " <separator />"
1497 " <toolitem action='FileImport' />"
1498 " <toolitem action='FileExport' />"
1499 " <separator />"
1500 " <toolitem action='EditUndo' />"
1501 " <toolitem action='EditRedo' />"
1502 " <separator />"
1503 " <toolitem action='EditCopy' />"
1504 " <toolitem action='EditCut' />"
1505 " <toolitem action='EditPaste' />"
1506 " <separator />"
1507 " <toolitem action='ZoomSelection' />"
1508 " <toolitem action='ZoomDrawing' />"
1509 " <toolitem action='ZoomPage' />"
1510 " <separator />"
1511 " <toolitem action='EditDuplicate' />"
1512 " <toolitem action='EditClone' />"
1513 " <toolitem action='EditUnlinkClone' />"
1514 " <separator />"
1515 " <toolitem action='SelectionGroup' />"
1516 " <toolitem action='SelectionUnGroup' />"
1517 " <separator />"
1518 " <toolitem action='DialogFillStroke' />"
1519 " <toolitem action='DialogText' />"
1520 " <toolitem action='DialogXMLEditor' />"
1521 " <toolitem action='DialogAlignDistribute' />"
1522 " <separator />"
1523 " <toolitem action='DialogPreferences' />"
1524 " <toolitem action='DialogDocumentProperties' />"
1525 " </toolbar>"
1526 "</ui>";
1527 GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1530 GtkUIManager* mgr = gtk_ui_manager_new();
1531 GError* errVal = 0;
1533 gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1534 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1536 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1537 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1538 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1539 }
1540 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1541 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1542 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1545 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1546 }
1548 static void
1549 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1550 {
1551 }
1553 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1554 {
1555 gtk_widget_show(toolbox_toplevel);
1556 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1558 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1559 if (!shown_toolbox) {
1560 return;
1561 }
1562 gtk_widget_show(toolbox);
1564 gtk_widget_show_all(shown_toolbox);
1565 }
1567 void
1568 aux_toolbox_space(GtkWidget *tb, gint space)
1569 {
1570 gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1571 }
1573 static GtkWidget *
1574 sp_empty_toolbox_new(SPDesktop *desktop)
1575 {
1576 GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1577 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1578 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1580 gtk_widget_show_all(tbl);
1581 sp_set_font_size_smaller (tbl);
1583 return tbl;
1584 }
1586 // helper UI functions
1588 GtkWidget *
1589 sp_tb_spinbutton(
1590 gchar *label, gchar const *tooltip,
1591 gchar const *path, gchar const *data, gdouble def,
1592 GtkWidget *us,
1593 GtkWidget *tbl,
1594 gboolean altx, gchar const *altx_mark,
1595 gdouble lower, gdouble upper, gdouble step, gdouble page,
1596 void (*callback)(GtkAdjustment *, GtkWidget *),
1597 gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1598 {
1599 GtkTooltips *tt = gtk_tooltips_new();
1601 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1603 GtkWidget *l = gtk_label_new(label);
1604 gtk_widget_show(l);
1605 gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1606 gtk_container_add(GTK_CONTAINER(hb), l);
1608 GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1609 lower, upper, step, page, page);
1610 gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1611 if (us)
1612 sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1614 GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1615 gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1616 if (altx)
1617 gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1618 gtk_widget_set_size_request(sb,
1619 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1620 AUX_SPINBUTTON_HEIGHT);
1621 gtk_widget_show(sb);
1622 gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1623 gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1624 gtk_container_add(GTK_CONTAINER(hb), sb);
1625 gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1627 return hb;
1628 }
1630 #define MODE_LABEL_WIDTH 70
1632 //########################
1633 //## Star ##
1634 //########################
1636 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1637 {
1638 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1640 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1641 // do not remember prefs if this call is initiated by an undo change, because undoing object
1642 // creation sets bogus values to its attributes before it is deleted
1643 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1644 }
1646 // quit if run by the attr_changed listener
1647 if (g_object_get_data( dataKludge, "freeze" )) {
1648 return;
1649 }
1651 // in turn, prevent listener from responding
1652 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1654 bool modmade = false;
1656 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1657 GSList const *items = selection->itemList();
1658 for (; items != NULL; items = items->next) {
1659 if (SP_IS_STAR((SPItem *) items->data)) {
1660 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1661 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1662 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1663 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1664 + M_PI / (gint)adj->value));
1665 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1666 modmade = true;
1667 }
1668 }
1669 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1670 _("Star: Change number of corners"));
1672 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1673 }
1675 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1676 {
1677 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1679 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1680 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1681 }
1683 // quit if run by the attr_changed listener
1684 if (g_object_get_data( dataKludge, "freeze" )) {
1685 return;
1686 }
1688 // in turn, prevent listener from responding
1689 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1691 bool modmade = false;
1692 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1693 GSList const *items = selection->itemList();
1694 for (; items != NULL; items = items->next) {
1695 if (SP_IS_STAR((SPItem *) items->data)) {
1696 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1698 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1699 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1700 if (r2 < r1) {
1701 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1702 } else {
1703 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1704 }
1706 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1707 modmade = true;
1708 }
1709 }
1711 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1712 _("Star: Change spoke ratio"));
1714 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1715 }
1717 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1718 {
1719 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1720 bool flat = ege_select_one_action_get_active( act ) == 0;
1722 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1723 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1724 flat ? "true" : "false" );
1725 }
1727 // quit if run by the attr_changed listener
1728 if (g_object_get_data( dataKludge, "freeze" )) {
1729 return;
1730 }
1732 // in turn, prevent listener from responding
1733 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1735 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1736 GSList const *items = selection->itemList();
1737 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1738 bool modmade = false;
1740 if ( prop_action ) {
1741 gtk_action_set_sensitive( prop_action, !flat );
1742 }
1744 for (; items != NULL; items = items->next) {
1745 if (SP_IS_STAR((SPItem *) items->data)) {
1746 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1747 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1748 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1749 modmade = true;
1750 }
1751 }
1753 if (modmade) {
1754 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1755 flat ? _("Make polygon") : _("Make star"));
1756 }
1758 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1759 }
1761 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1762 {
1763 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1765 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1766 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1767 }
1769 // quit if run by the attr_changed listener
1770 if (g_object_get_data( dataKludge, "freeze" )) {
1771 return;
1772 }
1774 // in turn, prevent listener from responding
1775 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1777 bool modmade = false;
1779 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1780 GSList const *items = selection->itemList();
1781 for (; items != NULL; items = items->next) {
1782 if (SP_IS_STAR((SPItem *) items->data)) {
1783 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1784 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1785 SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1786 modmade = true;
1787 }
1788 }
1789 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1790 _("Star: Change rounding"));
1792 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1793 }
1795 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1796 {
1797 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1799 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1800 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1801 }
1803 // quit if run by the attr_changed listener
1804 if (g_object_get_data( dataKludge, "freeze" )) {
1805 return;
1806 }
1808 // in turn, prevent listener from responding
1809 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1811 bool modmade = false;
1813 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1814 GSList const *items = selection->itemList();
1815 for (; items != NULL; items = items->next) {
1816 if (SP_IS_STAR((SPItem *) items->data)) {
1817 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1818 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1819 SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1820 modmade = true;
1821 }
1822 }
1823 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1824 _("Star: Change randomization"));
1826 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1827 }
1830 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1831 gchar const */*old_value*/, gchar const */*new_value*/,
1832 bool /*is_interactive*/, gpointer data)
1833 {
1834 GtkWidget *tbl = GTK_WIDGET(data);
1836 // quit if run by the _changed callbacks
1837 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1838 return;
1839 }
1841 // in turn, prevent callbacks from responding
1842 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1844 GtkAdjustment *adj = 0;
1846 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1847 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1849 if (!strcmp(name, "inkscape:randomized")) {
1850 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1851 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1852 } else if (!strcmp(name, "inkscape:rounded")) {
1853 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1854 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1855 } else if (!strcmp(name, "inkscape:flatsided")) {
1856 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1857 char const *flatsides = repr->attribute("inkscape:flatsided");
1858 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1859 if ( flatsides && !strcmp(flatsides,"false") ) {
1860 ege_select_one_action_set_active( flat_action, 1 );
1861 gtk_action_set_sensitive( prop_action, TRUE );
1862 } else {
1863 ege_select_one_action_set_active( flat_action, 0 );
1864 gtk_action_set_sensitive( prop_action, FALSE );
1865 }
1866 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1867 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1868 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1869 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1870 if (r2 < r1) {
1871 gtk_adjustment_set_value(adj, r2/r1);
1872 } else {
1873 gtk_adjustment_set_value(adj, r1/r2);
1874 }
1875 } else if (!strcmp(name, "sodipodi:sides")) {
1876 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1877 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1878 }
1880 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1881 }
1884 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1885 {
1886 NULL, /* child_added */
1887 NULL, /* child_removed */
1888 star_tb_event_attr_changed,
1889 NULL, /* content_changed */
1890 NULL /* order_changed */
1891 };
1894 /**
1895 * \param selection Should not be NULL.
1896 */
1897 static void
1898 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1899 {
1900 int n_selected = 0;
1901 Inkscape::XML::Node *repr = NULL;
1903 purge_repr_listener( tbl, tbl );
1905 for (GSList const *items = selection->itemList();
1906 items != NULL;
1907 items = items->next)
1908 {
1909 if (SP_IS_STAR((SPItem *) items->data)) {
1910 n_selected++;
1911 repr = SP_OBJECT_REPR((SPItem *) items->data);
1912 }
1913 }
1915 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1917 if (n_selected == 0) {
1918 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1919 } else if (n_selected == 1) {
1920 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1922 if (repr) {
1923 g_object_set_data( tbl, "repr", repr );
1924 Inkscape::GC::anchor(repr);
1925 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1926 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1927 }
1928 } else {
1929 // FIXME: implement averaging of all parameters for multiple selected stars
1930 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1931 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1932 }
1933 }
1936 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1937 {
1938 // FIXME: in this and all other _default functions, set some flag telling the value_changed
1939 // callbacks to lump all the changes for all selected objects in one undo step
1941 GtkAdjustment *adj = 0;
1943 // fixme: make settable in prefs!
1944 gint mag = 5;
1945 gdouble prop = 0.5;
1946 gboolean flat = FALSE;
1947 gdouble randomized = 0;
1948 gdouble rounded = 0;
1950 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1951 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1953 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1954 gtk_action_set_sensitive( sb2, !flat );
1956 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1957 gtk_adjustment_set_value(adj, mag);
1958 gtk_adjustment_value_changed(adj);
1960 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1961 gtk_adjustment_set_value(adj, prop);
1962 gtk_adjustment_value_changed(adj);
1964 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1965 gtk_adjustment_set_value(adj, rounded);
1966 gtk_adjustment_value_changed(adj);
1968 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1969 gtk_adjustment_set_value(adj, randomized);
1970 gtk_adjustment_value_changed(adj);
1971 }
1974 void
1975 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1976 {
1977 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1978 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1979 GtkWidget *l = gtk_label_new(NULL);
1980 gtk_label_set_markup(GTK_LABEL(l), title);
1981 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1982 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1983 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1984 }
1987 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1988 {
1989 {
1990 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1991 ege_output_action_set_use_markup( act, TRUE );
1992 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1993 g_object_set_data( holder, "mode_action", act );
1994 }
1996 {
1997 EgeAdjustmentAction* eact = 0;
1998 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1999 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2001 /* Flatsided checkbox */
2002 {
2003 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2005 GtkTreeIter iter;
2006 gtk_list_store_append( model, &iter );
2007 gtk_list_store_set( model, &iter,
2008 0, _("Polygon"),
2009 1, _("Regular polygon (with one handle) instead of a star"),
2010 2, "star_flat",
2011 -1 );
2013 gtk_list_store_append( model, &iter );
2014 gtk_list_store_set( model, &iter,
2015 0, _("Star"),
2016 1, _("Star instead of a regular polygon (with one handle)"),
2017 2, "star_angled",
2018 -1 );
2020 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2021 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2022 g_object_set_data( holder, "flat_action", act );
2024 ege_select_one_action_set_appearance( act, "full" );
2025 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2026 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2027 ege_select_one_action_set_icon_column( act, 2 );
2028 ege_select_one_action_set_tooltip_column( act, 1 );
2030 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2031 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2032 }
2034 /* Magnitude */
2035 {
2036 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2037 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2038 eact = create_adjustment_action( "MagnitudeAction",
2039 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2040 "tools.shapes.star", "magnitude", 3,
2041 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2042 3, 1024, 1, 5,
2043 labels, values, G_N_ELEMENTS(labels),
2044 sp_stb_magnitude_value_changed,
2045 1.0, 0 );
2046 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2047 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2048 }
2050 /* Spoke ratio */
2051 {
2052 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2053 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2054 eact = create_adjustment_action( "SpokeAction",
2055 _("Spoke ratio"), _("Spoke ratio:"),
2056 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2057 // Base radius is the same for the closest handle.
2058 _("Base radius to tip radius ratio"),
2059 "tools.shapes.star", "proportion", 0.5,
2060 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2061 0.01, 1.0, 0.01, 0.1,
2062 labels, values, G_N_ELEMENTS(labels),
2063 sp_stb_proportion_value_changed );
2064 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2065 g_object_set_data( holder, "prop_action", eact );
2066 }
2068 if ( !isFlatSided ) {
2069 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2070 } else {
2071 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2072 }
2074 /* Roundedness */
2075 {
2076 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2077 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2078 eact = create_adjustment_action( "RoundednessAction",
2079 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2080 "tools.shapes.star", "rounded", 0.0,
2081 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2082 -10.0, 10.0, 0.01, 0.1,
2083 labels, values, G_N_ELEMENTS(labels),
2084 sp_stb_rounded_value_changed );
2085 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2086 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2087 }
2089 /* Randomization */
2090 {
2091 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2092 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2093 eact = create_adjustment_action( "RandomizationAction",
2094 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2095 "tools.shapes.star", "randomized", 0.0,
2096 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2097 -10.0, 10.0, 0.001, 0.01,
2098 labels, values, G_N_ELEMENTS(labels),
2099 sp_stb_randomized_value_changed, 0.1, 3 );
2100 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2101 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2102 }
2103 }
2105 {
2106 /* Reset */
2107 {
2108 GtkAction* act = gtk_action_new( "StarResetAction",
2109 _("Defaults"),
2110 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2111 GTK_STOCK_CLEAR );
2112 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2113 gtk_action_group_add_action( mainActions, act );
2114 gtk_action_set_sensitive( act, TRUE );
2115 }
2116 }
2118 sigc::connection *connection = new sigc::connection(
2119 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2120 );
2121 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2122 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2123 }
2126 //########################
2127 //## Rect ##
2128 //########################
2130 static void sp_rtb_sensitivize( GObject *tbl )
2131 {
2132 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2133 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2134 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2136 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2137 gtk_action_set_sensitive( not_rounded, FALSE );
2138 } else {
2139 gtk_action_set_sensitive( not_rounded, TRUE );
2140 }
2141 }
2144 static void
2145 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2146 void (*setter)(SPRect *, gdouble))
2147 {
2148 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2150 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2151 SPUnit const *unit = tracker->getActiveUnit();
2153 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2154 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2155 }
2157 // quit if run by the attr_changed listener
2158 if (g_object_get_data( tbl, "freeze" )) {
2159 return;
2160 }
2162 // in turn, prevent listener from responding
2163 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2165 bool modmade = false;
2166 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2167 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2168 if (SP_IS_RECT(items->data)) {
2169 if (adj->value != 0) {
2170 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2171 } else {
2172 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2173 }
2174 modmade = true;
2175 }
2176 }
2178 sp_rtb_sensitivize( tbl );
2180 if (modmade) {
2181 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2182 _("Change rectangle"));
2183 }
2185 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2186 }
2188 static void
2189 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2190 {
2191 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2192 }
2194 static void
2195 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2196 {
2197 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2198 }
2200 static void
2201 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2202 {
2203 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2204 }
2206 static void
2207 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2208 {
2209 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2210 }
2214 static void
2215 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2216 {
2217 GtkAdjustment *adj = 0;
2219 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2220 gtk_adjustment_set_value(adj, 0.0);
2221 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2222 gtk_adjustment_value_changed(adj);
2224 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2225 gtk_adjustment_set_value(adj, 0.0);
2226 gtk_adjustment_value_changed(adj);
2228 sp_rtb_sensitivize( obj );
2229 }
2231 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2232 gchar const */*old_value*/, gchar const */*new_value*/,
2233 bool /*is_interactive*/, gpointer data)
2234 {
2235 GObject *tbl = G_OBJECT(data);
2237 // quit if run by the _changed callbacks
2238 if (g_object_get_data( tbl, "freeze" )) {
2239 return;
2240 }
2242 // in turn, prevent callbacks from responding
2243 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2245 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2246 SPUnit const *unit = tracker->getActiveUnit();
2248 gpointer item = g_object_get_data( tbl, "item" );
2249 if (item && SP_IS_RECT(item)) {
2250 {
2251 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2252 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2253 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2254 }
2256 {
2257 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2258 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2259 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2260 }
2262 {
2263 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2264 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2265 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2266 }
2268 {
2269 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2270 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2271 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2272 }
2273 }
2275 sp_rtb_sensitivize( tbl );
2277 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2278 }
2281 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2282 NULL, /* child_added */
2283 NULL, /* child_removed */
2284 rect_tb_event_attr_changed,
2285 NULL, /* content_changed */
2286 NULL /* order_changed */
2287 };
2289 /**
2290 * \param selection should not be NULL.
2291 */
2292 static void
2293 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2294 {
2295 int n_selected = 0;
2296 Inkscape::XML::Node *repr = NULL;
2297 SPItem *item = NULL;
2299 if ( g_object_get_data( tbl, "repr" ) ) {
2300 g_object_set_data( tbl, "item", NULL );
2301 }
2302 purge_repr_listener( tbl, tbl );
2304 for (GSList const *items = selection->itemList();
2305 items != NULL;
2306 items = items->next) {
2307 if (SP_IS_RECT((SPItem *) items->data)) {
2308 n_selected++;
2309 item = (SPItem *) items->data;
2310 repr = SP_OBJECT_REPR(item);
2311 }
2312 }
2314 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2316 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2318 if (n_selected == 0) {
2319 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2321 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2322 gtk_action_set_sensitive(w, FALSE);
2323 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2324 gtk_action_set_sensitive(h, FALSE);
2326 } else if (n_selected == 1) {
2327 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2328 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2330 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2331 gtk_action_set_sensitive(w, TRUE);
2332 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2333 gtk_action_set_sensitive(h, TRUE);
2335 if (repr) {
2336 g_object_set_data( tbl, "repr", repr );
2337 g_object_set_data( tbl, "item", item );
2338 Inkscape::GC::anchor(repr);
2339 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2340 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2341 }
2342 } else {
2343 // FIXME: implement averaging of all parameters for multiple selected
2344 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2345 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2346 sp_rtb_sensitivize( tbl );
2347 }
2348 }
2351 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2352 {
2353 EgeAdjustmentAction* eact = 0;
2355 {
2356 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2357 ege_output_action_set_use_markup( act, TRUE );
2358 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2359 g_object_set_data( holder, "mode_action", act );
2360 }
2362 // rx/ry units menu: create
2363 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2364 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2365 // fixme: add % meaning per cent of the width/height
2366 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2367 g_object_set_data( holder, "tracker", tracker );
2369 /* W */
2370 {
2371 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2372 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2373 eact = create_adjustment_action( "RectWidthAction",
2374 _("Width"), _("W:"), _("Width of rectangle"),
2375 "tools.shapes.rect", "width", 0,
2376 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2377 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2378 labels, values, G_N_ELEMENTS(labels),
2379 sp_rtb_width_value_changed );
2380 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2381 g_object_set_data( holder, "width_action", eact );
2382 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2383 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2384 }
2386 /* H */
2387 {
2388 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2389 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2390 eact = create_adjustment_action( "RectHeightAction",
2391 _("Height"), _("H:"), _("Height of rectangle"),
2392 "tools.shapes.rect", "height", 0,
2393 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2394 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2395 labels, values, G_N_ELEMENTS(labels),
2396 sp_rtb_height_value_changed );
2397 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2398 g_object_set_data( holder, "height_action", eact );
2399 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2400 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2401 }
2403 /* rx */
2404 {
2405 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2406 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2407 eact = create_adjustment_action( "RadiusXAction",
2408 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2409 "tools.shapes.rect", "rx", 0,
2410 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2411 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2412 labels, values, G_N_ELEMENTS(labels),
2413 sp_rtb_rx_value_changed);
2414 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2415 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2416 }
2418 /* ry */
2419 {
2420 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2421 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2422 eact = create_adjustment_action( "RadiusYAction",
2423 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2424 "tools.shapes.rect", "ry", 0,
2425 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2426 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2427 labels, values, G_N_ELEMENTS(labels),
2428 sp_rtb_ry_value_changed);
2429 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2430 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2431 }
2433 // add the units menu
2434 {
2435 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2436 gtk_action_group_add_action( mainActions, act );
2437 }
2439 /* Reset */
2440 {
2441 InkAction* inky = ink_action_new( "RectResetAction",
2442 _("Not rounded"),
2443 _("Make corners sharp"),
2444 "squared_corner",
2445 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2446 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2447 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2448 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2449 g_object_set_data( holder, "not_rounded", inky );
2450 }
2452 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2453 sp_rtb_sensitivize( holder );
2455 sigc::connection *connection = new sigc::connection(
2456 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2457 );
2458 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2459 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2460 }
2462 //########################
2463 //## 3D Box ##
2464 //########################
2466 // normalize angle so that it lies in the interval [0,360]
2467 static double box3d_normalize_angle (double a) {
2468 double angle = a + ((int) (a/360.0))*360;
2469 if (angle < 0) {
2470 angle += 360.0;
2471 }
2472 return angle;
2473 }
2475 static void
2476 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2477 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2478 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2479 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2480 // are reset).
2481 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2483 if (is_infinite) {
2484 gtk_toggle_action_set_active(tact, TRUE);
2485 gtk_action_set_sensitive(act, TRUE);
2487 double angle = persp3d_get_infinite_angle(persp, axis);
2488 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2489 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2490 }
2491 } else {
2492 gtk_toggle_action_set_active(tact, FALSE);
2493 gtk_action_set_sensitive(act, FALSE);
2494 }
2495 }
2497 static void
2498 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2499 if (!persp_repr) {
2500 g_print ("No perspective given to box3d_resync_toolbar().\n");
2501 return;
2502 }
2504 GtkWidget *tbl = GTK_WIDGET(data);
2505 GtkAdjustment *adj = 0;
2506 GtkAction *act = 0;
2507 GtkToggleAction *tact = 0;
2508 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2509 {
2510 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2511 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2512 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2514 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2515 }
2516 {
2517 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2518 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2519 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2521 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2522 }
2523 {
2524 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2525 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2526 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2528 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2529 }
2530 }
2532 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2533 gchar const */*old_value*/, gchar const */*new_value*/,
2534 bool /*is_interactive*/, gpointer data)
2535 {
2536 GtkWidget *tbl = GTK_WIDGET(data);
2538 // quit if run by the attr_changed listener
2539 // note: it used to work without the differently called freeze_ attributes (here and in
2540 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2541 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2542 return;
2543 }
2545 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2546 // sp_document_maybe_done() when the document is undo insensitive)
2547 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2549 // TODO: Only update the appropriate part of the toolbar
2550 // if (!strcmp(name, "inkscape:vp_z")) {
2551 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2552 // }
2554 Persp3D *persp = persp3d_get_from_repr(repr);
2555 persp3d_update_box_reprs(persp);
2557 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2558 }
2560 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2561 {
2562 NULL, /* child_added */
2563 NULL, /* child_removed */
2564 box3d_persp_tb_event_attr_changed,
2565 NULL, /* content_changed */
2566 NULL /* order_changed */
2567 };
2569 /**
2570 * \param selection Should not be NULL.
2571 */
2572 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2573 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2574 static void
2575 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2576 {
2577 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2578 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2579 // update the perspectives with infinite VPs and leave the other ones untouched).
2581 Inkscape::XML::Node *persp_repr = NULL;
2582 purge_repr_listener(tbl, tbl);
2584 SPItem *item = selection->singleItem();
2585 if (item && SP_IS_BOX3D(item)) {
2586 // FIXME: Also deal with multiple selected boxes
2587 SPBox3D *box = SP_BOX3D(item);
2588 Persp3D *persp = box3d_get_perspective(box);
2589 persp_repr = SP_OBJECT_REPR(persp);
2590 if (persp_repr) {
2591 g_object_set_data(tbl, "repr", persp_repr);
2592 Inkscape::GC::anchor(persp_repr);
2593 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2594 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2595 }
2597 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2598 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2600 box3d_resync_toolbar(persp_repr, tbl);
2601 }
2602 }
2604 static void
2605 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2606 {
2607 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2608 SPDocument *document = sp_desktop_document(desktop);
2610 // quit if run by the attr_changed listener
2611 // note: it used to work without the differently called freeze_ attributes (here and in
2612 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2613 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2614 return;
2615 }
2617 // in turn, prevent listener from responding
2618 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2620 //Persp3D *persp = document->current_persp3d;
2621 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2622 if (sel_persps.empty()) {
2623 // this can happen when the document is created; we silently ignore it
2624 return;
2625 }
2626 Persp3D *persp = sel_persps.front();
2628 persp->tmat.set_infinite_direction (axis, adj->value);
2629 SP_OBJECT(persp)->updateRepr();
2631 // TODO: use the correct axis here, too
2632 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2634 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2635 }
2638 static void
2639 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2640 {
2641 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2642 }
2644 static void
2645 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2646 {
2647 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2648 }
2650 static void
2651 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2652 {
2653 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2654 }
2657 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2658 {
2659 // TODO: Take all selected perspectives into account
2660 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2661 if (sel_persps.empty()) {
2662 // this can happen when the document is created; we silently ignore it
2663 return;
2664 }
2665 Persp3D *persp = sel_persps.front();
2667 bool set_infinite = gtk_toggle_action_get_active(act);
2668 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2669 }
2671 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2672 {
2673 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2674 }
2676 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2677 {
2678 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2679 }
2681 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2682 {
2683 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2684 }
2686 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2687 {
2688 EgeAdjustmentAction* eact = 0;
2689 SPDocument *document = sp_desktop_document (desktop);
2690 Persp3D *persp = document->current_persp3d;
2692 EgeAdjustmentAction* box3d_angle_x = 0;
2693 EgeAdjustmentAction* box3d_angle_y = 0;
2694 EgeAdjustmentAction* box3d_angle_z = 0;
2696 /* Angle X */
2697 {
2698 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2699 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2700 eact = create_adjustment_action( "3DBoxAngleXAction",
2701 _("Angle in X direction"), _("Angle X:"),
2702 // Translators: PL is short for 'perspective line'
2703 _("Angle of PLs in X direction"),
2704 "tools.shapes.3dbox", "box3d_angle_x", 30,
2705 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2706 -360.0, 360.0, 1.0, 10.0,
2707 labels, values, G_N_ELEMENTS(labels),
2708 box3d_angle_x_value_changed );
2709 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2710 g_object_set_data( holder, "box3d_angle_x_action", eact );
2711 box3d_angle_x = eact;
2712 }
2714 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2715 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2716 } else {
2717 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2718 }
2721 /* VP X state */
2722 {
2723 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2724 // Translators: VP is short for 'vanishing point'
2725 _("State of VP in X direction"),
2726 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2727 "toggle_vp_x",
2728 Inkscape::ICON_SIZE_DECORATION );
2729 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2730 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2731 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2732 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2733 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2734 }
2736 /* Angle Y */
2737 {
2738 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2739 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2740 eact = create_adjustment_action( "3DBoxAngleYAction",
2741 _("Angle in Y direction"), _("Angle Y:"),
2742 // Translators: PL is short for 'perspective line'
2743 _("Angle of PLs in Y direction"),
2744 "tools.shapes.3dbox", "box3d_angle_y", 30,
2745 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2746 -360.0, 360.0, 1.0, 10.0,
2747 labels, values, G_N_ELEMENTS(labels),
2748 box3d_angle_y_value_changed );
2749 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2750 g_object_set_data( holder, "box3d_angle_y_action", eact );
2751 box3d_angle_y = eact;
2752 }
2754 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2755 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2756 } else {
2757 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2758 }
2760 /* VP Y state */
2761 {
2762 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2763 // Translators: VP is short for 'vanishing point'
2764 _("State of VP in Y direction"),
2765 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2766 "toggle_vp_y",
2767 Inkscape::ICON_SIZE_DECORATION );
2768 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2769 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2770 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2771 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2772 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2773 }
2775 /* Angle Z */
2776 {
2777 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2778 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2779 eact = create_adjustment_action( "3DBoxAngleZAction",
2780 _("Angle in Z direction"), _("Angle Z:"),
2781 // Translators: PL is short for 'perspective line'
2782 _("Angle of PLs in Z direction"),
2783 "tools.shapes.3dbox", "box3d_angle_z", 30,
2784 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2785 -360.0, 360.0, 1.0, 10.0,
2786 labels, values, G_N_ELEMENTS(labels),
2787 box3d_angle_z_value_changed );
2788 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2789 g_object_set_data( holder, "box3d_angle_z_action", eact );
2790 box3d_angle_z = eact;
2791 }
2793 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2794 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2795 } else {
2796 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2797 }
2799 /* VP Z state */
2800 {
2801 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2802 // Translators: VP is short for 'vanishing point'
2803 _("State of VP in Z direction"),
2804 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2805 "toggle_vp_z",
2806 Inkscape::ICON_SIZE_DECORATION );
2807 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2808 g_object_set_data( holder, "box3d_vp_z_state_action", act );
2809 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2810 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2811 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2812 }
2814 sigc::connection *connection = new sigc::connection(
2815 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2816 );
2817 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2818 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2819 }
2821 //########################
2822 //## Spiral ##
2823 //########################
2825 static void
2826 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2827 {
2828 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2830 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2831 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2832 }
2834 // quit if run by the attr_changed listener
2835 if (g_object_get_data( tbl, "freeze" )) {
2836 return;
2837 }
2839 // in turn, prevent listener from responding
2840 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2842 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2844 bool modmade = false;
2845 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2846 items != NULL;
2847 items = items->next)
2848 {
2849 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2850 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2851 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2852 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2853 modmade = true;
2854 }
2855 }
2857 g_free(namespaced_name);
2859 if (modmade) {
2860 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2861 _("Change spiral"));
2862 }
2864 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2865 }
2867 static void
2868 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2869 {
2870 sp_spl_tb_value_changed(adj, tbl, "revolution");
2871 }
2873 static void
2874 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2875 {
2876 sp_spl_tb_value_changed(adj, tbl, "expansion");
2877 }
2879 static void
2880 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2881 {
2882 sp_spl_tb_value_changed(adj, tbl, "t0");
2883 }
2885 static void
2886 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2887 {
2888 GtkWidget *tbl = GTK_WIDGET(obj);
2890 GtkAdjustment *adj;
2892 // fixme: make settable
2893 gdouble rev = 5;
2894 gdouble exp = 1.0;
2895 gdouble t0 = 0.0;
2897 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2898 gtk_adjustment_set_value(adj, rev);
2899 gtk_adjustment_value_changed(adj);
2901 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2902 gtk_adjustment_set_value(adj, exp);
2903 gtk_adjustment_value_changed(adj);
2905 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2906 gtk_adjustment_set_value(adj, t0);
2907 gtk_adjustment_value_changed(adj);
2909 spinbutton_defocus(GTK_OBJECT(tbl));
2910 }
2913 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2914 gchar const */*old_value*/, gchar const */*new_value*/,
2915 bool /*is_interactive*/, gpointer data)
2916 {
2917 GtkWidget *tbl = GTK_WIDGET(data);
2919 // quit if run by the _changed callbacks
2920 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2921 return;
2922 }
2924 // in turn, prevent callbacks from responding
2925 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2927 GtkAdjustment *adj;
2928 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2929 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2931 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2932 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2934 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2935 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2937 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2938 }
2941 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2942 NULL, /* child_added */
2943 NULL, /* child_removed */
2944 spiral_tb_event_attr_changed,
2945 NULL, /* content_changed */
2946 NULL /* order_changed */
2947 };
2949 static void
2950 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2951 {
2952 int n_selected = 0;
2953 Inkscape::XML::Node *repr = NULL;
2955 purge_repr_listener( tbl, tbl );
2957 for (GSList const *items = selection->itemList();
2958 items != NULL;
2959 items = items->next)
2960 {
2961 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2962 n_selected++;
2963 repr = SP_OBJECT_REPR((SPItem *) items->data);
2964 }
2965 }
2967 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2969 if (n_selected == 0) {
2970 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2971 } else if (n_selected == 1) {
2972 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2974 if (repr) {
2975 g_object_set_data( tbl, "repr", repr );
2976 Inkscape::GC::anchor(repr);
2977 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2978 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2979 }
2980 } else {
2981 // FIXME: implement averaging of all parameters for multiple selected
2982 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2983 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2984 }
2985 }
2988 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2989 {
2990 EgeAdjustmentAction* eact = 0;
2992 {
2993 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2994 ege_output_action_set_use_markup( act, TRUE );
2995 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2996 g_object_set_data( holder, "mode_action", act );
2997 }
2999 /* Revolution */
3000 {
3001 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3002 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3003 eact = create_adjustment_action( "SpiralRevolutionAction",
3004 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3005 "tools.shapes.spiral", "revolution", 3.0,
3006 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3007 0.01, 1024.0, 0.1, 1.0,
3008 labels, values, G_N_ELEMENTS(labels),
3009 sp_spl_tb_revolution_value_changed, 1, 2);
3010 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3011 }
3013 /* Expansion */
3014 {
3015 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3016 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3017 eact = create_adjustment_action( "SpiralExpansionAction",
3018 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3019 "tools.shapes.spiral", "expansion", 1.0,
3020 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3021 0.0, 1000.0, 0.01, 1.0,
3022 labels, values, G_N_ELEMENTS(labels),
3023 sp_spl_tb_expansion_value_changed);
3024 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3025 }
3027 /* T0 */
3028 {
3029 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3030 gdouble values[] = {0, 0.5, 0.9};
3031 eact = create_adjustment_action( "SpiralT0Action",
3032 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3033 "tools.shapes.spiral", "t0", 0.0,
3034 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3035 0.0, 0.999, 0.01, 1.0,
3036 labels, values, G_N_ELEMENTS(labels),
3037 sp_spl_tb_t0_value_changed);
3038 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3039 }
3041 /* Reset */
3042 {
3043 InkAction* inky = ink_action_new( "SpiralResetAction",
3044 _("Defaults"),
3045 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3046 GTK_STOCK_CLEAR,
3047 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3048 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3049 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3050 }
3053 sigc::connection *connection = new sigc::connection(
3054 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3055 );
3056 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3057 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3058 }
3060 //########################
3061 //## Pen/Pencil ##
3062 //########################
3065 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
3066 {
3067 // Put stuff here
3068 }
3070 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
3071 {
3072 // Put stuff here
3073 }
3075 //########################
3076 //## Tweak ##
3077 //########################
3079 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3080 {
3081 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3082 }
3084 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3085 {
3086 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3087 }
3089 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3090 {
3091 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3092 }
3094 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3095 {
3096 int mode = ege_select_one_action_get_active( act );
3097 prefs_set_int_attribute("tools.tweak", "mode", mode);
3099 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3100 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3101 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3102 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3103 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3104 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3105 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3106 if (doh) gtk_action_set_sensitive (doh, TRUE);
3107 if (dos) gtk_action_set_sensitive (dos, TRUE);
3108 if (dol) gtk_action_set_sensitive (dol, TRUE);
3109 if (doo) gtk_action_set_sensitive (doo, TRUE);
3110 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3111 if (fid) gtk_action_set_sensitive (fid, FALSE);
3112 } else {
3113 if (doh) gtk_action_set_sensitive (doh, FALSE);
3114 if (dos) gtk_action_set_sensitive (dos, FALSE);
3115 if (dol) gtk_action_set_sensitive (dol, FALSE);
3116 if (doo) gtk_action_set_sensitive (doo, FALSE);
3117 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3118 if (fid) gtk_action_set_sensitive (fid, TRUE);
3119 }
3120 }
3122 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3123 {
3124 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3125 }
3127 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3128 bool show = gtk_toggle_action_get_active( act );
3129 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3130 }
3131 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3132 bool show = gtk_toggle_action_get_active( act );
3133 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3134 }
3135 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3136 bool show = gtk_toggle_action_get_active( act );
3137 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3138 }
3139 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3140 bool show = gtk_toggle_action_get_active( act );
3141 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3142 }
3144 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3145 {
3146 {
3147 /* Width */
3148 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3149 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3150 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3151 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3152 "tools.tweak", "width", 15,
3153 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3154 1, 100, 1.0, 10.0,
3155 labels, values, G_N_ELEMENTS(labels),
3156 sp_tweak_width_value_changed, 0.01, 0, 100 );
3157 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3158 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3159 }
3162 {
3163 /* Force */
3164 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3165 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3166 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3167 _("Force"), _("Force:"), _("The force of the tweak action"),
3168 "tools.tweak", "force", 20,
3169 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3170 1, 100, 1.0, 10.0,
3171 labels, values, G_N_ELEMENTS(labels),
3172 sp_tweak_force_value_changed, 0.01, 0, 100 );
3173 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3174 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3175 }
3177 /* Mode */
3178 {
3179 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3181 GtkTreeIter iter;
3182 gtk_list_store_append( model, &iter );
3183 gtk_list_store_set( model, &iter,
3184 0, _("Push mode"),
3185 1, _("Push parts of paths in any direction"),
3186 2, "tweak_push_mode",
3187 -1 );
3189 gtk_list_store_append( model, &iter );
3190 gtk_list_store_set( model, &iter,
3191 0, _("Shrink mode"),
3192 1, _("Shrink (inset) parts of paths"),
3193 2, "tweak_shrink_mode",
3194 -1 );
3196 gtk_list_store_append( model, &iter );
3197 gtk_list_store_set( model, &iter,
3198 0, _("Grow mode"),
3199 1, _("Grow (outset) parts of paths"),
3200 2, "tweak_grow_mode",
3201 -1 );
3203 gtk_list_store_append( model, &iter );
3204 gtk_list_store_set( model, &iter,
3205 0, _("Attract mode"),
3206 1, _("Attract parts of paths towards cursor"),
3207 2, "tweak_attract_mode",
3208 -1 );
3210 gtk_list_store_append( model, &iter );
3211 gtk_list_store_set( model, &iter,
3212 0, _("Repel mode"),
3213 1, _("Repel parts of paths from cursor"),
3214 2, "tweak_repel_mode",
3215 -1 );
3217 gtk_list_store_append( model, &iter );
3218 gtk_list_store_set( model, &iter,
3219 0, _("Roughen mode"),
3220 1, _("Roughen parts of paths"),
3221 2, "tweak_roughen_mode",
3222 -1 );
3224 gtk_list_store_append( model, &iter );
3225 gtk_list_store_set( model, &iter,
3226 0, _("Color paint mode"),
3227 1, _("Paint the tool's color upon selected objects"),
3228 2, "tweak_colorpaint_mode",
3229 -1 );
3231 gtk_list_store_append( model, &iter );
3232 gtk_list_store_set( model, &iter,
3233 0, _("Color jitter mode"),
3234 1, _("Jitter the colors of selected objects"),
3235 2, "tweak_colorjitter_mode",
3236 -1 );
3238 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3239 g_object_set( act, "short_label", _("Mode:"), NULL );
3240 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3241 g_object_set_data( holder, "mode_action", act );
3243 ege_select_one_action_set_appearance( act, "full" );
3244 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3245 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3246 ege_select_one_action_set_icon_column( act, 2 );
3247 ege_select_one_action_set_tooltip_column( act, 1 );
3249 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3250 ege_select_one_action_set_active( act, mode );
3251 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3253 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3254 }
3256 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3258 {
3259 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3260 ege_output_action_set_use_markup( act, TRUE );
3261 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3262 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3263 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3264 g_object_set_data( holder, "tweak_channels_label", act);
3265 }
3267 {
3268 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3269 _("Hue"),
3270 _("In color mode, act on objects' hue"),
3271 NULL,
3272 Inkscape::ICON_SIZE_DECORATION );
3273 //TRANSLATORS: "H" here stands for hue
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 //TRANSLATORS: "S" here stands for Saturation
3289 g_object_set( act, "short_label", _("S"), NULL );
3290 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3291 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3292 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3293 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3294 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3295 g_object_set_data( holder, "tweak_dos", act );
3296 }
3297 {
3298 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3299 _("Lightness"),
3300 _("In color mode, act on objects' lightness"),
3301 NULL,
3302 Inkscape::ICON_SIZE_DECORATION );
3303 //TRANSLATORS: "L" here stands for Lightness
3304 g_object_set( act, "short_label", _("L"), NULL );
3305 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3306 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3307 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3308 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3309 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3310 g_object_set_data( holder, "tweak_dol", act );
3311 }
3312 {
3313 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3314 _("Opacity"),
3315 _("In color mode, act on objects' opacity"),
3316 NULL,
3317 Inkscape::ICON_SIZE_DECORATION );
3318 //TRANSLATORS: "O" here stands for Opacity
3319 g_object_set( act, "short_label", _("O"), NULL );
3320 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3321 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3322 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3323 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3324 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3325 g_object_set_data( holder, "tweak_doo", act );
3326 }
3328 { /* Fidelity */
3329 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3330 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3331 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3332 _("Fidelity"), _("Fidelity:"),
3333 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3334 "tools.tweak", "fidelity", 50,
3335 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3336 1, 100, 1.0, 10.0,
3337 labels, values, G_N_ELEMENTS(labels),
3338 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3339 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3340 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3341 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3342 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3343 g_object_set_data( holder, "tweak_fidelity", eact );
3344 }
3347 /* Use Pressure button */
3348 {
3349 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3350 _("Pressure"),
3351 _("Use the pressure of the input device to alter the force of tweak action"),
3352 "use_pressure",
3353 Inkscape::ICON_SIZE_DECORATION );
3354 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3355 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3356 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3357 }
3359 }
3362 //########################
3363 //## Calligraphy ##
3364 //########################
3365 static void update_presets_list(GObject *dataKludge ){
3366 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3367 if (sel) {
3368 ege_select_one_action_set_active(sel, 0 );
3369 }
3370 }
3372 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3373 {
3374 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3375 update_presets_list(tbl);
3376 }
3378 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3379 {
3380 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3381 update_presets_list(tbl);
3382 }
3384 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3385 {
3386 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3387 update_presets_list(tbl);
3388 }
3390 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3391 {
3392 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3393 update_presets_list(tbl);
3394 }
3396 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3397 {
3398 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3399 update_presets_list(tbl);
3400 }
3402 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3403 {
3404 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3405 update_presets_list(tbl);
3406 }
3408 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3409 {
3410 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3411 update_presets_list(tbl);
3412 }
3414 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3415 {
3416 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3417 update_presets_list(tbl);
3418 }
3420 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3421 {
3422 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3423 update_presets_list(tbl);
3424 }
3426 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
3427 {
3428 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3429 update_presets_list(tbl);
3430 }
3432 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
3433 {
3434 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3435 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3436 update_presets_list(tbl);
3437 if (calligraphy_angle )
3438 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3439 }
3442 #define PROFILE_FLOAT_SIZE 7
3443 #define PROFILE_INT_SIZE 4
3444 struct ProfileFloatElement {
3445 char const *name;
3446 double def;
3447 double min;
3448 double max;
3449 };
3450 struct ProfileIntElement {
3451 char const *name;
3452 int def;
3453 int min;
3454 int max;
3455 };
3459 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3460 {"mass",0.02, 0.0, 1.0},
3461 {"wiggle",0.0, 0.0, 1.0},
3462 {"angle",30.0, -90.0, 90.0},
3463 {"thinning",0.1, -1.0, 1.0},
3464 {"tremor",0.0, 0.0, 1.0},
3465 {"flatness",0.9, 0.0, 1.0},
3466 {"cap_rounding",0.0, 0.0, 5.0}
3467 };
3468 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3469 {"width",15, 1, 100},
3470 {"usepressure",1,0,1},
3471 {"tracebackground",0,0,1},
3472 {"usetilt",1,0,1},
3473 };
3477 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3478 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3479 if (! desktop) return;
3481 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3482 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3483 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3485 unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3486 gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3487 gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);
3489 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3490 ProfileFloatElement const &pe = f_profile[i];
3491 double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3492 prefs_set_double_attribute(pref_path,pe.name,v);
3493 }
3494 for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3495 ProfileIntElement const &pe = i_profile[i];
3496 int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3497 prefs_set_int_attribute(pref_path,pe.name,v);
3498 }
3499 prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3501 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3502 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3503 GtkTreeIter iter;
3504 gtk_list_store_append( model, &iter );
3505 gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3507 free(profile_id);
3508 free(pref_path);
3510 ege_select_one_action_set_active(selector, new_index);
3511 }
3514 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3516 gint preset_index = ege_select_one_action_get_active( act );
3517 gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3519 if ( profile_name) {
3520 g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3521 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3522 ProfileFloatElement const &pe = f_profile[i];
3523 double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3524 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3525 if ( adj ) {
3526 gtk_adjustment_set_value(adj, v);
3527 }
3528 }
3529 for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3530 ProfileIntElement const &pe = i_profile[i];
3531 int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3532 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3533 if ( toggle ) {
3534 gtk_toggle_action_set_active(toggle, v);
3535 } else printf("No toggle");
3536 }
3537 free(profile_name);
3538 g_object_set_data(dataKludge, "profile_selector",act); //restore selector visibility
3539 }
3541 }
3544 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3545 {
3546 {
3547 EgeAdjustmentAction* calligraphy_angle = 0;
3549 {
3550 /* Width */
3551 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3552 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3553 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3554 _("Pen Width"), _("Width:"),
3555 _("The width of the calligraphic pen (relative to the visible canvas area)"),
3556 "tools.calligraphic", "width", 15,
3557 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3558 1, 100, 1.0, 10.0,
3559 labels, values, G_N_ELEMENTS(labels),
3560 sp_ddc_width_value_changed, 0.01, 0, 100 );
3561 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3562 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3563 }
3565 {
3566 /* Thinning */
3567 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3568 gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3569 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3570 _("Stroke Thinning"), _("Thinning:"),
3571 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3572 "tools.calligraphic", "thinning", 0.1,
3573 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3574 -1.0, 1.0, 0.01, 0.1,
3575 labels, values, G_N_ELEMENTS(labels),
3576 sp_ddc_velthin_value_changed, 0.01, 2);
3577 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3578 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3579 }
3581 {
3582 /* Angle */
3583 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3584 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3585 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3586 _("Pen Angle"), _("Angle:"),
3587 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3588 "tools.calligraphic", "angle", 30,
3589 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3590 -90.0, 90.0, 1.0, 10.0,
3591 labels, values, G_N_ELEMENTS(labels),
3592 sp_ddc_angle_value_changed, 1, 0 );
3593 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3594 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3595 g_object_set_data( holder, "angle", eact );
3596 calligraphy_angle = eact;
3597 }
3599 {
3600 /* Fixation */
3601 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3602 gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3603 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3604 _("Fixation"), _("Fixation:"),
3605 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3606 "tools.calligraphic", "flatness", 0.9,
3607 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3608 0.0, 1.0, 0.01, 0.1,
3609 labels, values, G_N_ELEMENTS(labels),
3610 sp_ddc_flatness_value_changed, 0.01, 2 );
3611 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3612 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3613 }
3615 {
3616 /* Cap Rounding */
3617 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3618 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3619 // TRANSLATORS: "cap" means "end" (both start and finish) here
3620 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3621 _("Cap rounding"), _("Caps:"),
3622 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3623 "tools.calligraphic", "cap_rounding", 0.0,
3624 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3625 0.0, 5.0, 0.01, 0.1,
3626 labels, values, G_N_ELEMENTS(labels),
3627 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3628 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3629 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3630 }
3632 {
3633 /* Tremor */
3634 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3635 gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3636 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3637 _("Stroke Tremor"), _("Tremor:"),
3638 _("Increase to make strokes rugged and trembling"),
3639 "tools.calligraphic", "tremor", 0.0,
3640 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3641 0.0, 1.0, 0.01, 0.1,
3642 labels, values, G_N_ELEMENTS(labels),
3643 sp_ddc_tremor_value_changed, 0.01, 2 );
3645 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3646 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3647 }
3649 {
3650 /* Wiggle */
3651 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3652 gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3653 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3654 _("Pen Wiggle"), _("Wiggle:"),
3655 _("Increase to make the pen waver and wiggle"),
3656 "tools.calligraphic", "wiggle", 0.0,
3657 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3658 0.0, 1.0, 0.01, 0.1,
3659 labels, values, G_N_ELEMENTS(labels),
3660 sp_ddc_wiggle_value_changed, 0.01, 2 );
3661 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3662 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3663 }
3665 {
3666 /* Mass */
3667 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3668 gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3669 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3670 _("Pen Mass"), _("Mass:"),
3671 _("Increase to make the pen drag behind, as if slowed by inertia"),
3672 "tools.calligraphic", "mass", 0.02,
3673 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3674 0.0, 1.0, 0.01, 0.1,
3675 labels, values, G_N_ELEMENTS(labels),
3676 sp_ddc_mass_value_changed, 0.01, 2 );
3677 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3678 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3679 }
3682 /* Trace Background button */
3683 {
3684 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3685 _("Trace Background"),
3686 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3687 "trace_background",
3688 Inkscape::ICON_SIZE_DECORATION );
3689 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3690 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
3691 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3692 g_object_set_data( holder, "tracebackground", act );
3693 }
3695 /* Use Pressure button */
3696 {
3697 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3698 _("Pressure"),
3699 _("Use the pressure of the input device to alter the width of the pen"),
3700 "use_pressure",
3701 Inkscape::ICON_SIZE_DECORATION );
3702 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3703 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
3704 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3705 g_object_set_data( holder, "usepressure", act );
3706 }
3708 /* Use Tilt button */
3709 {
3710 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3711 _("Tilt"),
3712 _("Use the tilt of the input device to alter the angle of the pen's nib"),
3713 "use_tilt",
3714 Inkscape::ICON_SIZE_DECORATION );
3715 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3716 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
3717 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3718 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3719 g_object_set_data( holder, "usetilt", act );
3720 }
3722 /*calligraphic profile */
3723 {
3724 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3725 gchar *pref_path;
3728 GtkTreeIter iter;
3729 gtk_list_store_append( model, &iter );
3730 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3732 //TODO: switch back to prefs API
3733 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3734 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3735 int ii=1;
3736 while (child_repr) {
3737 GtkTreeIter iter;
3738 char *preset_name = (char *) child_repr->attribute("name");
3739 gtk_list_store_append( model, &iter );
3740 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
3741 child_repr = sp_repr_next(child_repr);
3742 }
3744 pref_path = NULL;
3745 EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , ("Change calligraphic profile"), NULL, GTK_TREE_MODEL(model) );
3746 ege_select_one_action_set_appearance( act1, "compact" );
3747 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
3748 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3749 g_object_set_data( holder, "profile_selector", act1 );
3751 }
3753 /*Save or delete calligraphic profile */
3754 {
3755 GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
3756 _("Defaults"),
3757 _("Save current settings as new profile"),
3758 GTK_STOCK_SAVE );
3759 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
3762 gtk_action_group_add_action( mainActions, act );
3763 gtk_action_set_sensitive( act, TRUE );
3764 g_object_set_data( holder, "profile_save_delete", act );
3765 }
3766 }
3767 }
3770 //########################
3771 //## Circle / Arc ##
3772 //########################
3774 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3775 {
3776 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3777 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3779 if (v1 == 0 && v2 == 0) {
3780 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3781 gtk_action_set_sensitive( ocb, FALSE );
3782 gtk_action_set_sensitive( make_whole, FALSE );
3783 }
3784 } else {
3785 gtk_action_set_sensitive( ocb, TRUE );
3786 gtk_action_set_sensitive( make_whole, TRUE );
3787 }
3788 }
3790 static void
3791 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3792 {
3793 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3795 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3796 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3797 }
3799 // quit if run by the attr_changed listener
3800 if (g_object_get_data( tbl, "freeze" )) {
3801 return;
3802 }
3804 // in turn, prevent listener from responding
3805 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3807 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3809 bool modmade = false;
3810 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3811 items != NULL;
3812 items = items->next)
3813 {
3814 SPItem *item = SP_ITEM(items->data);
3816 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3818 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3819 SPArc *arc = SP_ARC(item);
3821 if (!strcmp(value_name, "start"))
3822 ge->start = (adj->value * M_PI)/ 180;
3823 else
3824 ge->end = (adj->value * M_PI)/ 180;
3826 sp_genericellipse_normalize(ge);
3827 ((SPObject *)arc)->updateRepr();
3828 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3830 modmade = true;
3831 }
3832 }
3834 g_free(namespaced_name);
3836 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3838 sp_arctb_sensitivize( tbl, adj->value, other->value );
3840 if (modmade) {
3841 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3842 _("Arc: Change start/end"));
3843 }
3845 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3846 }
3849 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
3850 {
3851 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
3852 }
3854 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3855 {
3856 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
3857 }
3859 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3860 {
3861 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3862 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3863 if ( ege_select_one_action_get_active( act ) != 0 ) {
3864 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3865 } else {
3866 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3867 }
3868 }
3870 // quit if run by the attr_changed listener
3871 if (g_object_get_data( tbl, "freeze" )) {
3872 return;
3873 }
3875 // in turn, prevent listener from responding
3876 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3878 bool modmade = false;
3880 if ( ege_select_one_action_get_active(act) != 0 ) {
3881 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3882 items != NULL;
3883 items = items->next)
3884 {
3885 if (SP_IS_ARC((SPItem *) items->data)) {
3886 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3887 repr->setAttribute("sodipodi:open", "true");
3888 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3889 modmade = true;
3890 }
3891 }
3892 } else {
3893 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3894 items != NULL;
3895 items = items->next)
3896 {
3897 if (SP_IS_ARC((SPItem *) items->data)) {
3898 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3899 repr->setAttribute("sodipodi:open", NULL);
3900 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3901 modmade = true;
3902 }
3903 }
3904 }
3906 if (modmade) {
3907 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3908 _("Arc: Change open/closed"));
3909 }
3911 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3912 }
3914 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3915 {
3916 GtkAdjustment *adj;
3917 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3918 gtk_adjustment_set_value(adj, 0.0);
3919 gtk_adjustment_value_changed(adj);
3921 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3922 gtk_adjustment_set_value(adj, 0.0);
3923 gtk_adjustment_value_changed(adj);
3925 spinbutton_defocus( GTK_OBJECT(obj) );
3926 }
3928 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3929 gchar const */*old_value*/, gchar const */*new_value*/,
3930 bool /*is_interactive*/, gpointer data)
3931 {
3932 GObject *tbl = G_OBJECT(data);
3934 // quit if run by the _changed callbacks
3935 if (g_object_get_data( tbl, "freeze" )) {
3936 return;
3937 }
3939 // in turn, prevent callbacks from responding
3940 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3942 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3943 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3945 GtkAdjustment *adj1,*adj2;
3946 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3947 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3948 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3949 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3951 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3953 char const *openstr = NULL;
3954 openstr = repr->attribute("sodipodi:open");
3955 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3957 if (openstr) {
3958 ege_select_one_action_set_active( ocb, 1 );
3959 } else {
3960 ege_select_one_action_set_active( ocb, 0 );
3961 }
3963 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3964 }
3966 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3967 NULL, /* child_added */
3968 NULL, /* child_removed */
3969 arc_tb_event_attr_changed,
3970 NULL, /* content_changed */
3971 NULL /* order_changed */
3972 };
3975 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3976 {
3977 int n_selected = 0;
3978 Inkscape::XML::Node *repr = NULL;
3980 purge_repr_listener( tbl, tbl );
3982 for (GSList const *items = selection->itemList();
3983 items != NULL;
3984 items = items->next)
3985 {
3986 if (SP_IS_ARC((SPItem *) items->data)) {
3987 n_selected++;
3988 repr = SP_OBJECT_REPR((SPItem *) items->data);
3989 }
3990 }
3992 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3994 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3995 if (n_selected == 0) {
3996 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3997 } else if (n_selected == 1) {
3998 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3999 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4001 if (repr) {
4002 g_object_set_data( tbl, "repr", repr );
4003 Inkscape::GC::anchor(repr);
4004 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4005 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4006 }
4007 } else {
4008 // FIXME: implement averaging of all parameters for multiple selected
4009 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4010 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4011 sp_arctb_sensitivize( tbl, 1, 0 );
4012 }
4013 }
4016 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4017 {
4018 EgeAdjustmentAction* eact = 0;
4021 {
4022 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4023 ege_output_action_set_use_markup( act, TRUE );
4024 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4025 g_object_set_data( holder, "mode_action", act );
4026 }
4028 /* Start */
4029 {
4030 eact = create_adjustment_action( "ArcStartAction",
4031 _("Start"), _("Start:"),
4032 _("The angle (in degrees) from the horizontal to the arc's start point"),
4033 "tools.shapes.arc", "start", 0.0,
4034 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4035 -360.0, 360.0, 1.0, 10.0,
4036 0, 0, 0,
4037 sp_arctb_start_value_changed);
4038 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4039 }
4041 /* End */
4042 {
4043 eact = create_adjustment_action( "ArcEndAction",
4044 _("End"), _("End:"),
4045 _("The angle (in degrees) from the horizontal to the arc's end point"),
4046 "tools.shapes.arc", "end", 0.0,
4047 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4048 -360.0, 360.0, 1.0, 10.0,
4049 0, 0, 0,
4050 sp_arctb_end_value_changed);
4051 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4052 }
4054 /* Segments / Pie checkbox */
4055 {
4056 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4058 GtkTreeIter iter;
4059 gtk_list_store_append( model, &iter );
4060 gtk_list_store_set( model, &iter,
4061 0, _("Closed arc"),
4062 1, _("Switch to segment (closed shape with two radii)"),
4063 2, "circle_closed_arc",
4064 -1 );
4066 gtk_list_store_append( model, &iter );
4067 gtk_list_store_set( model, &iter,
4068 0, _("Open Arc"),
4069 1, _("Switch to arc (unclosed shape)"),
4070 2, "circle_open_arc",
4071 -1 );
4073 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4074 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4075 g_object_set_data( holder, "open_action", act );
4077 ege_select_one_action_set_appearance( act, "full" );
4078 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4079 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4080 ege_select_one_action_set_icon_column( act, 2 );
4081 ege_select_one_action_set_tooltip_column( act, 1 );
4083 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4084 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4085 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4086 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4087 }
4089 /* Make Whole */
4090 {
4091 InkAction* inky = ink_action_new( "ArcResetAction",
4092 _("Make whole"),
4093 _("Make the shape a whole ellipse, not arc or segment"),
4094 "reset_circle",
4095 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4096 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4097 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4098 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4099 g_object_set_data( holder, "make_whole", inky );
4100 }
4102 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4103 // sensitivize make whole and open checkbox
4104 {
4105 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4106 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4107 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4108 }
4111 sigc::connection *connection = new sigc::connection(
4112 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4113 );
4114 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4115 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4116 }
4121 // toggle button callbacks and updaters
4123 //########################
4124 //## Dropper ##
4125 //########################
4127 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4128 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4129 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4130 if ( set_action ) {
4131 if ( gtk_toggle_action_get_active( act ) ) {
4132 gtk_action_set_sensitive( set_action, TRUE );
4133 } else {
4134 gtk_action_set_sensitive( set_action, FALSE );
4135 }
4136 }
4138 spinbutton_defocus(GTK_OBJECT(tbl));
4139 }
4141 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4142 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4143 spinbutton_defocus(GTK_OBJECT(tbl));
4144 }
4147 /**
4148 * Dropper auxiliary toolbar construction and setup.
4149 *
4150 * TODO: Would like to add swatch of current color.
4151 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4152 * can drag and drop places. Will provide a nice mixing palette.
4153 */
4154 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4155 {
4156 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4158 {
4159 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4160 ege_output_action_set_use_markup( act, TRUE );
4161 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4162 }
4164 {
4165 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4166 _("Pick opacity"),
4167 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4168 NULL,
4169 Inkscape::ICON_SIZE_DECORATION );
4170 g_object_set( act, "short_label", _("Pick"), NULL );
4171 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4172 g_object_set_data( holder, "pick_action", act );
4173 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4174 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4175 }
4177 {
4178 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4179 _("Assign opacity"),
4180 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4181 NULL,
4182 Inkscape::ICON_SIZE_DECORATION );
4183 g_object_set( act, "short_label", _("Assign"), NULL );
4184 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4185 g_object_set_data( holder, "set_action", act );
4186 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4187 // make sure it's disabled if we're not picking alpha
4188 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4189 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4190 }
4191 }
4194 //########################
4195 //## Text Toolbox ##
4196 //########################
4197 /*
4198 static void
4199 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4200 {
4201 //Call back for letter sizing spinbutton
4202 }
4204 static void
4205 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4206 {
4207 //Call back for line height spinbutton
4208 }
4210 static void
4211 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4212 {
4213 //Call back for horizontal kerning spinbutton
4214 }
4216 static void
4217 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4218 {
4219 //Call back for vertical kerning spinbutton
4220 }
4222 static void
4223 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4224 {
4225 //Call back for letter rotation spinbutton
4226 }*/
4228 namespace {
4230 bool popdown_visible = false;
4231 bool popdown_hasfocus = false;
4233 void
4234 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4235 {
4236 SPStyle *query =
4237 sp_style_new (SP_ACTIVE_DOCUMENT);
4239 int result_fontspec =
4240 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4242 int result_family =
4243 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4245 int result_style =
4246 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4248 int result_numbers =
4249 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4251 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4253 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4254 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4255 {
4256 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4258 if (repr)
4259 {
4260 sp_style_read_from_repr (query, repr);
4261 }
4262 else
4263 {
4264 return;
4265 }
4266 }
4268 if (query->text)
4269 {
4270 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4271 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4272 gtk_entry_set_text (GTK_ENTRY (entry), "");
4274 } else if (query->text->font_specification.value || query->text->font_family.value) {
4276 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4278 // Get the font that corresponds
4279 Glib::ustring familyName;
4281 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4282 if (font) {
4283 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4284 font->Unref();
4285 font = NULL;
4286 }
4288 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4290 Gtk::TreePath path;
4291 try {
4292 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4293 } catch (...) {
4294 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4295 return;
4296 }
4298 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4299 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4301 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4303 gtk_tree_selection_select_path (tselection, path.gobj());
4304 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4306 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4307 }
4309 //Size
4310 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4311 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4312 g_object_set_data (tbl, "size-block", gpointer(1));
4313 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4314 g_object_set_data (tbl, "size-block", gpointer(0));
4315 free (str);
4317 //Anchor
4318 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4319 {
4320 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4321 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4322 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4323 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4324 }
4325 else
4326 {
4327 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4328 {
4329 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4330 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4331 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4332 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4333 }
4334 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4335 {
4336 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4337 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4338 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4339 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4340 }
4341 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4342 {
4343 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4344 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4345 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4346 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4347 }
4348 }
4350 //Style
4351 {
4352 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4354 gboolean active = gtk_toggle_button_get_active (button);
4355 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4357 if (active != check)
4358 {
4359 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4360 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4361 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4362 }
4363 }
4365 {
4366 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4368 gboolean active = gtk_toggle_button_get_active (button);
4369 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4371 if (active != check)
4372 {
4373 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4374 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4375 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4376 }
4377 }
4379 //Orientation
4380 //locking both buttons, changing one affect all group (both)
4381 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4382 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4384 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4385 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4387 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4388 {
4389 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4390 }
4391 else
4392 {
4393 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4394 }
4395 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4396 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4397 }
4399 sp_style_unref(query);
4400 }
4402 void
4403 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4404 {
4405 sp_text_toolbox_selection_changed (selection, tbl);
4406 }
4408 void
4409 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4410 {
4411 sp_text_toolbox_selection_changed (NULL, tbl);
4412 }
4414 void
4415 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
4416 GObject *tbl)
4417 {
4418 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4419 GtkTreeModel *model = 0;
4420 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4421 GtkTreeIter iter;
4422 char *family = 0;
4424 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4425 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4427 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4428 return;
4429 }
4431 gtk_tree_model_get (model, &iter, 0, &family, -1);
4433 if (g_object_get_data (G_OBJECT (selection), "block"))
4434 {
4435 gtk_entry_set_text (GTK_ENTRY (entry), family);
4436 return;
4437 }
4439 gtk_entry_set_text (GTK_ENTRY (entry), family);
4441 SPStyle *query =
4442 sp_style_new (SP_ACTIVE_DOCUMENT);
4444 int result_fontspec =
4445 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4447 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4449 SPCSSAttr *css = sp_repr_css_attr_new ();
4452 // First try to get the font spec from the stored value
4453 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4455 if (fontSpec.empty()) {
4456 // Construct a new font specification if it does not yet exist
4457 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4458 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4459 fontFromStyle->Unref();
4460 }
4462 if (!fontSpec.empty()) {
4463 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4464 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4465 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4466 if (font) {
4467 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4469 // Set all the these just in case they were altered when finding the best
4470 // match for the new family and old style...
4472 gchar c[256];
4474 font->Family(c, 256);
4475 sp_repr_css_set_property (css, "font-family", c);
4477 font->Attribute( "weight", c, 256);
4478 sp_repr_css_set_property (css, "font-weight", c);
4480 font->Attribute("style", c, 256);
4481 sp_repr_css_set_property (css, "font-style", c);
4483 font->Attribute("stretch", c, 256);
4484 sp_repr_css_set_property (css, "font-stretch", c);
4486 font->Attribute("variant", c, 256);
4487 sp_repr_css_set_property (css, "font-variant", c);
4489 font->Unref();
4490 }
4491 }
4492 }
4494 // If querying returned nothing, set the default style of the tool (for new texts)
4495 if (result_fontspec == QUERY_STYLE_NOTHING)
4496 {
4497 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4498 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4499 }
4500 else
4501 {
4502 sp_desktop_set_style (desktop, css, true, true);
4503 }
4505 sp_style_unref(query);
4507 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4508 _("Text: Change font family"));
4509 sp_repr_css_attr_unref (css);
4510 free (family);
4511 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4513 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4514 }
4516 void
4517 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
4518 GObject *tbl)
4519 {
4520 const char *family = gtk_entry_get_text (entry);
4522 try {
4523 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4524 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4525 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4526 gtk_tree_selection_select_path (selection, path.gobj());
4527 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4528 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4529 } catch (...) {
4530 if (family && strlen (family))
4531 {
4532 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4533 }
4534 }
4535 }
4537 void
4538 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
4539 gpointer data)
4540 {
4541 if (g_object_get_data (G_OBJECT (button), "block")) return;
4542 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4543 int prop = GPOINTER_TO_INT(data);
4545 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4546 SPCSSAttr *css = sp_repr_css_attr_new ();
4548 switch (prop)
4549 {
4550 case 0:
4551 {
4552 sp_repr_css_set_property (css, "text-anchor", "start");
4553 sp_repr_css_set_property (css, "text-align", "start");
4554 break;
4555 }
4556 case 1:
4557 {
4558 sp_repr_css_set_property (css, "text-anchor", "middle");
4559 sp_repr_css_set_property (css, "text-align", "center");
4560 break;
4561 }
4563 case 2:
4564 {
4565 sp_repr_css_set_property (css, "text-anchor", "end");
4566 sp_repr_css_set_property (css, "text-align", "end");
4567 break;
4568 }
4570 case 3:
4571 {
4572 sp_repr_css_set_property (css, "text-anchor", "start");
4573 sp_repr_css_set_property (css, "text-align", "justify");
4574 break;
4575 }
4576 }
4578 SPStyle *query =
4579 sp_style_new (SP_ACTIVE_DOCUMENT);
4580 int result_numbers =
4581 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4583 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4584 if (result_numbers == QUERY_STYLE_NOTHING)
4585 {
4586 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4587 }
4589 sp_style_unref(query);
4591 sp_desktop_set_style (desktop, css, true, true);
4592 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4593 _("Text: Change alignment"));
4594 sp_repr_css_attr_unref (css);
4596 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4597 }
4599 void
4600 sp_text_toolbox_style_toggled (GtkToggleButton *button,
4601 gpointer data)
4602 {
4603 if (g_object_get_data (G_OBJECT (button), "block")) return;
4605 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4606 SPCSSAttr *css = sp_repr_css_attr_new ();
4607 int prop = GPOINTER_TO_INT(data);
4608 bool active = gtk_toggle_button_get_active (button);
4610 SPStyle *query =
4611 sp_style_new (SP_ACTIVE_DOCUMENT);
4613 int result_fontspec =
4614 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4616 int result_family =
4617 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4619 int result_style =
4620 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4622 int result_numbers =
4623 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4625 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4626 Glib::ustring newFontSpec = "";
4628 if (fontSpec.empty()) {
4629 // Construct a new font specification if it does not yet exist
4630 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4631 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4632 fontFromStyle->Unref();
4633 }
4635 switch (prop)
4636 {
4637 case 0:
4638 {
4639 if (!fontSpec.empty()) {
4640 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4641 }
4642 if (fontSpec != newFontSpec) {
4643 // Don't even set the bold if the font didn't exist on the system
4644 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4645 }
4646 break;
4647 }
4649 case 1:
4650 {
4651 if (!fontSpec.empty()) {
4652 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4653 }
4654 if (fontSpec != newFontSpec) {
4655 // Don't even set the italic if the font didn't exist on the system
4656 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4657 }
4658 break;
4659 }
4660 }
4662 if (!newFontSpec.empty()) {
4663 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4664 }
4666 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4667 if (result_fontspec == QUERY_STYLE_NOTHING)
4668 {
4669 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4670 }
4672 sp_style_unref(query);
4674 sp_desktop_set_style (desktop, css, true, true);
4675 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4676 _("Text: Change font style"));
4677 sp_repr_css_attr_unref (css);
4679 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4680 }
4682 void
4683 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
4684 gpointer data)
4685 {
4686 if (g_object_get_data (G_OBJECT (button), "block")) {
4687 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4688 return;
4689 }
4691 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4692 SPCSSAttr *css = sp_repr_css_attr_new ();
4693 int prop = GPOINTER_TO_INT(data);
4695 switch (prop)
4696 {
4697 case 0:
4698 {
4699 sp_repr_css_set_property (css, "writing-mode", "lr");
4700 break;
4701 }
4703 case 1:
4704 {
4705 sp_repr_css_set_property (css, "writing-mode", "tb");
4706 break;
4707 }
4708 }
4710 SPStyle *query =
4711 sp_style_new (SP_ACTIVE_DOCUMENT);
4712 int result_numbers =
4713 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4715 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4716 if (result_numbers == QUERY_STYLE_NOTHING)
4717 {
4718 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4719 }
4721 sp_desktop_set_style (desktop, css, true, true);
4722 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4723 _("Text: Change orientation"));
4724 sp_repr_css_attr_unref (css);
4726 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4727 }
4729 gboolean
4730 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4731 {
4732 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4733 if (!desktop) return FALSE;
4735 switch (get_group0_keyval (event)) {
4736 case GDK_Escape: // defocus
4737 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4738 sp_text_toolbox_selection_changed (NULL, tbl); // update
4739 return TRUE; // I consumed the event
4740 break;
4741 }
4742 return FALSE;
4743 }
4745 gboolean
4746 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4747 {
4748 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4749 if (!desktop) return FALSE;
4751 switch (get_group0_keyval (event)) {
4752 case GDK_KP_Enter:
4753 case GDK_Return:
4754 case GDK_Escape: // defocus
4755 gtk_widget_hide (w);
4756 popdown_visible = false;
4757 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4758 return TRUE; // I consumed the event
4759 break;
4760 case GDK_w:
4761 case GDK_W:
4762 if (event->state & GDK_CONTROL_MASK) {
4763 gtk_widget_hide (w);
4764 popdown_visible = false;
4765 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4766 return TRUE; // I consumed the event
4767 }
4768 break;
4769 }
4770 return FALSE;
4771 }
4774 void
4775 sp_text_toolbox_size_changed (GtkComboBox *cbox,
4776 GObject *tbl)
4777 {
4778 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4780 if (g_object_get_data (tbl, "size-block")) return;
4782 // If this is not from selecting a size in the list (in which case get_active will give the
4783 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4784 // process this event. This fixes GTK's stupid insistence on sending an activate change every
4785 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4786 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4787 return;
4789 gchar *endptr;
4790 gdouble value = -1;
4791 char *text = gtk_combo_box_get_active_text (cbox);
4792 if (text) {
4793 value = g_strtod (text, &endptr);
4794 if (endptr == text) // conversion failed, non-numeric input
4795 value = -1;
4796 free (text);
4797 }
4798 if (value <= 0) {
4799 return; // could not parse value
4800 }
4802 SPCSSAttr *css = sp_repr_css_attr_new ();
4803 Inkscape::CSSOStringStream osfs;
4804 osfs << value;
4805 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4807 SPStyle *query =
4808 sp_style_new (SP_ACTIVE_DOCUMENT);
4809 int result_numbers =
4810 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4812 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4813 if (result_numbers == QUERY_STYLE_NOTHING)
4814 {
4815 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4816 }
4818 sp_style_unref(query);
4820 sp_desktop_set_style (desktop, css, true, true);
4821 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4822 _("Text: Change font size"));
4823 sp_repr_css_attr_unref (css);
4825 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4826 }
4828 gboolean
4829 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
4830 {
4831 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4832 if (!desktop) return FALSE;
4834 if (!g_object_get_data (tbl, "esc-pressed")) {
4835 g_object_set_data (tbl, "enter-pressed", gpointer(1));
4836 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4837 sp_text_toolbox_size_changed (cbox, tbl);
4838 g_object_set_data (tbl, "enter-pressed", gpointer(0));
4839 }
4840 return FALSE; // I consumed the event
4841 }
4844 gboolean
4845 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4846 {
4847 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4848 if (!desktop) return FALSE;
4850 switch (get_group0_keyval (event)) {
4851 case GDK_Escape: // defocus
4852 g_object_set_data (tbl, "esc-pressed", gpointer(1));
4853 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4854 g_object_set_data (tbl, "esc-pressed", gpointer(0));
4855 return TRUE; // I consumed the event
4856 break;
4857 case GDK_Return: // defocus
4858 case GDK_KP_Enter:
4859 g_object_set_data (tbl, "enter-pressed", gpointer(1));
4860 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4861 sp_text_toolbox_size_changed (cbox, tbl);
4862 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4863 g_object_set_data (tbl, "enter-pressed", gpointer(0));
4864 return TRUE; // I consumed the event
4865 break;
4866 }
4867 return FALSE;
4868 }
4870 void
4871 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
4872 GObject *tbl)
4873 {
4874 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4875 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4876 int x, y;
4878 if (!popdown_visible)
4879 {
4880 gdk_window_get_origin (widget->window, &x, &y);
4881 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4882 gtk_widget_show_all (popdown);
4883 //sp_transientize (popdown);
4885 gdk_pointer_grab (widget->window, TRUE,
4886 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4887 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4888 GDK_POINTER_MOTION_MASK),
4889 NULL, NULL, GDK_CURRENT_TIME);
4891 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4893 popdown_visible = true;
4894 }
4895 else
4896 {
4897 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4898 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4899 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4900 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4901 gtk_widget_hide (popdown);
4902 popdown_visible = false;
4903 }
4904 }
4906 gboolean
4907 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
4908 GdkEventFocus */*event*/,
4909 GObject */*tbl*/)
4910 {
4911 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4912 return FALSE;
4913 }
4915 gboolean
4916 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
4917 GdkEventFocus */*event*/,
4918 GObject */*tbl*/)
4919 {
4920 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4922 if (popdown_hasfocus) {
4923 gtk_widget_hide (popdown);
4924 popdown_hasfocus = false;
4925 popdown_visible = false;
4926 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4927 return TRUE;
4928 }
4929 return FALSE;
4930 }
4932 gboolean
4933 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
4934 GdkEventFocus */*event*/,
4935 GObject */*tbl*/)
4936 {
4937 popdown_hasfocus = true;
4938 return TRUE;
4939 }
4942 void
4943 cell_data_func (GtkTreeViewColumn */*column*/,
4944 GtkCellRenderer *cell,
4945 GtkTreeModel *tree_model,
4946 GtkTreeIter *iter,
4947 gpointer /*data*/)
4948 {
4949 char *family,
4950 *family_escaped,
4951 *sample_escaped;
4953 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4955 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4957 family_escaped = g_markup_escape_text (family, -1);
4958 sample_escaped = g_markup_escape_text (sample, -1);
4960 std::stringstream markup;
4961 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4962 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4964 free (family);
4965 free (family_escaped);
4966 free (sample_escaped);
4967 }
4969 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4970 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4971 if (completion) {
4972 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4973 g_object_unref (completion);
4974 }
4975 }
4977 GtkWidget*
4978 sp_text_toolbox_new (SPDesktop *desktop)
4979 {
4980 GtkWidget *tbl = gtk_hbox_new (FALSE, 0);
4982 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4983 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4985 GtkTooltips *tt = gtk_tooltips_new();
4986 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4988 ////////////Family
4989 //Window
4990 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4991 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4993 //Entry
4994 GtkWidget *entry = gtk_entry_new ();
4995 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4996 GtkEntryCompletion *completion = gtk_entry_completion_new ();
4997 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4998 gtk_entry_completion_set_text_column (completion, 0);
4999 gtk_entry_completion_set_minimum_key_length (completion, 1);
5000 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5001 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5002 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5003 aux_toolbox_space (tbl, 1);
5004 gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
5005 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5007 //Button
5008 GtkWidget *button = gtk_button_new ();
5009 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5010 gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, 0);
5012 //Popdown
5013 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
5014 GtkWidget *treeview = gtk_tree_view_new ();
5016 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5017 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
5018 gtk_tree_view_column_pack_start (column, cell, FALSE);
5019 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5020 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5021 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5023 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5024 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5025 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5027 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5029 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5030 gtk_container_add (GTK_CONTAINER (sw), treeview);
5032 gtk_container_add (GTK_CONTAINER (window), sw);
5033 gtk_widget_set_size_request (window, 300, 450);
5035 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5036 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5037 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5039 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5041 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5042 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5043 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5045 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5046 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5048 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5049 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5050 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5051 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5052 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5054 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
5055 aux_toolbox_space (tbl, 1);
5056 GtkWidget *box = gtk_event_box_new ();
5057 gtk_container_add (GTK_CONTAINER (box), image);
5058 gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
5059 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5060 GtkTooltips *tooltips = gtk_tooltips_new ();
5061 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5062 gtk_widget_hide (GTK_WIDGET (box));
5063 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5065 ////////////Size
5066 const char *sizes[] = {
5067 "4", "6", "8", "9", "10", "11", "12", "13", "14",
5068 "16", "18", "20", "22", "24", "28",
5069 "32", "36", "40", "48", "56", "64", "72", "144"
5070 };
5072 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5073 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5074 gtk_widget_set_size_request (cbox, 80, -1);
5075 aux_toolbox_space (tbl, 1);
5076 gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
5077 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5078 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5079 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5080 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5082 //spacer
5083 aux_toolbox_space (tbl, 4);
5084 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5086 ////////////Text anchor
5087 GtkWidget *group = gtk_radio_button_new (NULL);
5088 GtkWidget *row = gtk_hbox_new (FALSE, 4);
5089 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5091 // left
5092 GtkWidget *rbutton = group;
5093 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5094 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
5095 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5097 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5098 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
5099 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5100 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5102 // center
5103 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5104 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5105 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
5106 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5108 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5109 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
5110 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5111 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5113 // right
5114 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5115 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5116 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
5117 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5119 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5120 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5121 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5122 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5124 // fill
5125 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5126 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5127 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
5128 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5130 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5131 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
5132 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5133 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5135 aux_toolbox_space (tbl, 1);
5136 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5138 //spacer
5139 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5141 ////////////Text style
5142 row = gtk_hbox_new (FALSE, 4);
5144 // bold
5145 rbutton = gtk_toggle_button_new ();
5146 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5147 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
5148 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5149 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5151 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5152 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
5153 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5155 // italic
5156 rbutton = gtk_toggle_button_new ();
5157 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5158 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
5159 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5160 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5162 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5163 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
5164 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5166 aux_toolbox_space (tbl, 1);
5167 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5169 //spacer
5170 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5172 ////////////Text orientation
5173 group = gtk_radio_button_new (NULL);
5174 row = gtk_hbox_new (FALSE, 4);
5175 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5177 // horizontal
5178 rbutton = group;
5179 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5180 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
5181 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5182 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5184 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5185 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5186 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5188 // vertical
5189 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5190 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5191 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
5192 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5193 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5195 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5196 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
5197 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5198 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5201 //watch selection
5202 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5204 sigc::connection *c_selection_changed =
5205 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5206 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5207 pool->add_connection ("selection-changed", c_selection_changed);
5209 sigc::connection *c_selection_modified =
5210 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5211 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5212 pool->add_connection ("selection-modified", c_selection_modified);
5214 sigc::connection *c_subselection_changed =
5215 new sigc::connection (desktop->connectToolSubselectionChanged
5216 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5217 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5219 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5222 gtk_widget_show_all (tbl);
5223 return tbl;
5225 } // end of sp_text_toolbox_new()
5227 }//<unnamed> namespace
5230 //#########################
5231 //## Connector ##
5232 //#########################
5234 static void sp_connector_path_set_avoid(void)
5235 {
5236 cc_selection_set_avoid(true);
5237 }
5240 static void sp_connector_path_set_ignore(void)
5241 {
5242 cc_selection_set_avoid(false);
5243 }
5247 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5248 {
5249 // quit if run by the _changed callbacks
5250 if (g_object_get_data( tbl, "freeze" )) {
5251 return;
5252 }
5254 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5255 SPDocument *doc = sp_desktop_document(desktop);
5257 if (!sp_document_get_undo_sensitive(doc))
5258 {
5259 return;
5260 }
5262 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5264 if ( repr->attribute("inkscape:connector-spacing") ) {
5265 gdouble priorValue = gtk_adjustment_get_value(adj);
5266 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5267 if ( priorValue == gtk_adjustment_get_value(adj) ) {
5268 return;
5269 }
5270 } else if ( adj->value == defaultConnSpacing ) {
5271 return;
5272 }
5274 // in turn, prevent callbacks from responding
5275 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5277 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5278 SP_OBJECT(desktop->namedview)->updateRepr();
5280 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5281 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5282 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5283 NR::Matrix m = NR::identity();
5284 avoid_item_move(&m, item);
5285 }
5287 if (items) {
5288 g_slist_free(items);
5289 }
5291 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5292 _("Change connector spacing"));
5294 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5296 spinbutton_defocus(GTK_OBJECT(tbl));
5297 }
5299 static void sp_connector_graph_layout(void)
5300 {
5301 if (!SP_ACTIVE_DESKTOP) return;
5303 // hack for clones, see comment in align-and-distribute.cpp
5304 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5305 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5307 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5309 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5311 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5312 }
5314 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5315 {
5316 if ( gtk_toggle_action_get_active( act ) ) {
5317 prefs_set_string_attribute("tools.connector", "directedlayout",
5318 "true");
5319 } else {
5320 prefs_set_string_attribute("tools.connector", "directedlayout",
5321 "false");
5322 }
5323 }
5325 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5326 {
5327 if ( gtk_toggle_action_get_active( act ) ) {
5328 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5329 "true");
5330 } else {
5331 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5332 "false");
5333 }
5334 }
5337 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5338 {
5339 prefs_set_double_attribute("tools.connector", "length", adj->value);
5340 spinbutton_defocus(GTK_OBJECT(tbl));
5341 }
5343 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5344 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5345 bool /*is_interactive*/, gpointer data)
5346 {
5347 GtkWidget *tbl = GTK_WIDGET(data);
5349 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5350 return;
5351 }
5352 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5353 return;
5354 }
5356 GtkAdjustment *adj = (GtkAdjustment*)
5357 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5358 gdouble spacing = defaultConnSpacing;
5359 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5361 gtk_adjustment_set_value(adj, spacing);
5362 }
5365 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5366 NULL, /* child_added */
5367 NULL, /* child_removed */
5368 connector_tb_event_attr_changed,
5369 NULL, /* content_changed */
5370 NULL /* order_changed */
5371 };
5374 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5375 {
5376 {
5377 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5378 _("Avoid"),
5379 _("Make connectors avoid selected objects"),
5380 "connector_avoid",
5381 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5382 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5383 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5384 }
5386 {
5387 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5388 _("Ignore"),
5389 _("Make connectors ignore selected objects"),
5390 "connector_ignore",
5391 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5392 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5393 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5394 }
5396 EgeAdjustmentAction* eact = 0;
5398 // Spacing spinbox
5399 eact = create_adjustment_action( "ConnectorSpacingAction",
5400 _("Connector Spacing"), _("Spacing:"),
5401 _("The amount of space left around objects by auto-routing connectors"),
5402 "tools.connector", "spacing", defaultConnSpacing,
5403 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5404 0, 100, 1.0, 10.0,
5405 0, 0, 0,
5406 connector_spacing_changed, 1, 0 );
5407 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5409 // Graph (connector network) layout
5410 {
5411 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5412 _("Graph"),
5413 _("Nicely arrange selected connector network"),
5414 "graph_layout",
5415 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5416 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5417 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5418 }
5420 // Default connector length spinbox
5421 eact = create_adjustment_action( "ConnectorLengthAction",
5422 _("Connector Length"), _("Length:"),
5423 _("Ideal length for connectors when layout is applied"),
5424 "tools.connector", "length", 100,
5425 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5426 10, 1000, 10.0, 100.0,
5427 0, 0, 0,
5428 connector_length_changed, 1, 0 );
5429 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5432 // Directed edges toggle button
5433 {
5434 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5435 _("Downwards"),
5436 _("Make connectors with end-markers (arrows) point downwards"),
5437 "directed_graph",
5438 Inkscape::ICON_SIZE_DECORATION );
5439 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5441 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5442 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5443 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5445 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5446 }
5448 // Avoid overlaps toggle button
5449 {
5450 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5451 _("Remove overlaps"),
5452 _("Do not allow overlapping shapes"),
5453 "remove_overlaps",
5454 Inkscape::ICON_SIZE_DECORATION );
5455 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5457 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5458 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5459 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5461 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5462 }
5464 // Code to watch for changes to the connector-spacing attribute in
5465 // the XML.
5466 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5467 g_assert(repr != NULL);
5469 purge_repr_listener( holder, holder );
5471 if (repr) {
5472 g_object_set_data( holder, "repr", repr );
5473 Inkscape::GC::anchor(repr);
5474 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5475 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5476 }
5477 } // end of sp_connector_toolbox_prep()
5480 //#########################
5481 //## Paintbucket ##
5482 //#########################
5484 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5485 {
5486 gint channels = ege_select_one_action_get_active( act );
5487 flood_channels_set_channels( channels );
5488 }
5490 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5491 {
5492 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5493 }
5495 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5496 {
5497 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5498 }
5500 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5501 {
5502 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5503 SPUnit const *unit = tracker->getActiveUnit();
5505 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5507 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5508 }
5510 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5511 {
5512 // FIXME: make defaults settable via Inkscape Options
5513 struct KeyValue {
5514 char const *key;
5515 double value;
5516 } const key_values[] = {
5517 {"threshold", 15},
5518 {"offset", 0.0}
5519 };
5521 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5522 KeyValue const &kv = key_values[i];
5523 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5524 if ( adj ) {
5525 gtk_adjustment_set_value(adj, kv.value);
5526 }
5527 }
5529 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5530 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5531 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5532 ege_select_one_action_set_active( autogap_action, 0 );
5533 }
5535 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5536 {
5537 EgeAdjustmentAction* eact = 0;
5539 {
5540 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5542 GList* items = 0;
5543 gint count = 0;
5544 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5545 {
5546 GtkTreeIter iter;
5547 gtk_list_store_append( model, &iter );
5548 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5549 count++;
5550 }
5551 g_list_free( items );
5552 items = 0;
5553 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5554 g_object_set( act1, "short_label", _("Fill by:"), NULL );
5555 ege_select_one_action_set_appearance( act1, "compact" );
5556 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5557 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5558 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5559 g_object_set_data( holder, "channels_action", act1 );
5560 }
5562 // Spacing spinbox
5563 {
5564 eact = create_adjustment_action(
5565 "ThresholdAction",
5566 _("Fill Threshold"), _("Threshold:"),
5567 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5568 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5569 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5570 0, 0, 0,
5571 paintbucket_threshold_changed, 1, 0 );
5573 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5574 }
5576 // Create the units menu.
5577 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5578 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5579 if (stored_unit)
5580 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5581 g_object_set_data( holder, "tracker", tracker );
5582 {
5583 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5584 gtk_action_group_add_action( mainActions, act );
5585 }
5587 // Offset spinbox
5588 {
5589 eact = create_adjustment_action(
5590 "OffsetAction",
5591 _("Grow/shrink by"), _("Grow/shrink by:"),
5592 _("The amount to grow (positive) or shrink (negative) the created fill path"),
5593 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5594 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5595 0, 0, 0,
5596 paintbucket_offset_changed, 1, 2);
5597 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5599 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5600 }
5602 /* Auto Gap */
5603 {
5604 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5606 GList* items = 0;
5607 gint count = 0;
5608 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5609 {
5610 GtkTreeIter iter;
5611 gtk_list_store_append( model, &iter );
5612 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5613 count++;
5614 }
5615 g_list_free( items );
5616 items = 0;
5617 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5618 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5619 ege_select_one_action_set_appearance( act2, "compact" );
5620 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5621 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5622 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5623 g_object_set_data( holder, "autogap_action", act2 );
5624 }
5626 /* Reset */
5627 {
5628 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5629 _("Defaults"),
5630 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5631 GTK_STOCK_CLEAR );
5632 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5633 gtk_action_group_add_action( mainActions, act );
5634 gtk_action_set_sensitive( act, TRUE );
5635 }
5637 }
5639 /*
5640 Local Variables:
5641 mode:c++
5642 c-file-style:"stroustrup"
5643 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5644 indent-tabs-mode:nil
5645 fill-column:99
5646 End:
5647 */
5648 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :