1 /** \file
2 * Controls bars for some of Inkscape's tools
3 * (for some tools, they are in their own files)
4 */
6 /*
7 *
8 * Authors:
9 * MenTaLguY <mental@rydia.net>
10 * Lauris Kaplinski <lauris@kaplinski.com>
11 * bulia byak <buliabyak@users.sf.net>
12 * Frank Felfe <innerspace@iname.com>
13 * John Cliff <simarilius@yahoo.com>
14 * David Turner <novalis@gnu.org>
15 * Josh Andler <scislac@scislac.com>
16 * Jon A. Cruz <jon@joncruz.org>
17 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2006 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <cstring>
31 #include <string>
33 #include <gtkmm.h>
34 #include <gtk/gtk.h>
35 #include <iostream>
36 #include <sstream>
38 #include "widgets/button.h"
39 #include "widgets/widget-sizes.h"
40 #include "widgets/spw-utilities.h"
41 #include "widgets/spinbutton-events.h"
42 #include "dialogs/text-edit.h"
43 #include "dialogs/dialog-events.h"
45 #include "ui/widget/style-swatch.h"
47 #include "prefs-utils.h"
48 #include "verbs.h"
49 #include "sp-namedview.h"
50 #include "desktop.h"
51 #include "desktop-handles.h"
52 #include "xml/repr.h"
53 #include "xml/node-event-vector.h"
54 #include <glibmm/i18n.h>
55 #include "helper/unit-menu.h"
56 #include "helper/units.h"
58 #include "inkscape.h"
59 #include "conn-avoid-ref.h"
62 #include "select-toolbar.h"
63 #include "gradient-toolbar.h"
65 #include "connector-context.h"
66 #include "node-context.h"
67 #include "shape-editor.h"
68 #include "tweak-context.h"
69 #include "sp-rect.h"
70 #include "box3d.h"
71 #include "box3d-context.h"
72 #include "sp-star.h"
73 #include "sp-spiral.h"
74 #include "sp-ellipse.h"
75 #include "sp-text.h"
76 #include "sp-flowtext.h"
77 #include "sp-clippath.h"
78 #include "sp-mask.h"
79 #include "style.h"
80 #include "selection.h"
81 #include "selection-chemistry.h"
82 #include "document-private.h"
83 #include "desktop-style.h"
84 #include "../libnrtype/font-lister.h"
85 #include "../libnrtype/font-instance.h"
86 #include "../connection-pool.h"
87 #include "../prefs-utils.h"
88 #include "../inkscape-stock.h"
89 #include "icon.h"
90 #include "graphlayout/graphlayout.h"
92 #include "mod360.h"
94 #include "toolbox.h"
96 #include "flood-context.h"
98 #include "ink-action.h"
99 #include "ege-adjustment-action.h"
100 #include "ege-output-action.h"
101 #include "ege-select-one-action.h"
102 #include "helper/unit-tracker.h"
104 #include "svg/css-ostringstream.h"
106 #include "widgets/calligraphic-profile-rename.h"
108 using Inkscape::UnitTracker;
110 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
111 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
113 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
126 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
132 static struct {
133 gchar const *type_name;
134 gchar const *data_name;
135 sp_verb_t verb;
136 sp_verb_t doubleclick_verb;
137 } const tools[] = {
138 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
139 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
140 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
141 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
142 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
143 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
144 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
145 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
146 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
147 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
148 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
149 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
150 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
151 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
152 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
153 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
154 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
155 { NULL, NULL, 0, 0 }
156 };
158 static struct {
159 gchar const *type_name;
160 gchar const *data_name;
161 GtkWidget *(*create_func)(SPDesktop *desktop);
162 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
163 gchar const *ui_name;
164 gint swatch_verb_id;
165 gchar const *swatch_tool;
166 gchar const *swatch_tip;
167 } const aux_toolboxes[] = {
168 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
169 SP_VERB_INVALID, 0, 0},
170 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
171 SP_VERB_INVALID, 0, 0},
172 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
173 SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
174 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
175 SP_VERB_INVALID, 0, 0},
176 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
177 SP_VERB_CONTEXT_STAR_PREFS, "tools.shapes.star", _("Style of new stars")},
178 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
179 SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", _("Style of new rectangles")},
180 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
181 SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", _("Style of new 3D boxes")},
182 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
183 SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", _("Style of new ellipses")},
184 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
185 SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral", _("Style of new spirals")},
186 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
187 SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
188 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
189 SP_VERB_CONTEXT_PEN_PREFS, "tools.freehand.pen", _("Style of new paths created by Pen")},
190 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
191 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
192 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
193 SP_VERB_INVALID, 0, 0},
194 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
195 SP_VERB_INVALID, 0, 0},
196 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
197 SP_VERB_INVALID, 0, 0},
198 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
199 SP_VERB_INVALID, 0, 0},
200 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
201 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
202 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
203 };
206 static gchar const * ui_descr =
207 "<ui>"
208 " <toolbar name='SelectToolbar'>"
209 " <toolitem action='EditSelectAll' />"
210 " <toolitem action='EditSelectAllInAllLayers' />"
211 " <toolitem action='EditDeselect' />"
212 " <separator />"
213 " <toolitem action='ObjectRotate90CCW' />"
214 " <toolitem action='ObjectRotate90' />"
215 " <toolitem action='ObjectFlipHorizontally' />"
216 " <toolitem action='ObjectFlipVertically' />"
217 " <separator />"
218 " <toolitem action='SelectionToBack' />"
219 " <toolitem action='SelectionLower' />"
220 " <toolitem action='SelectionRaise' />"
221 " <toolitem action='SelectionToFront' />"
222 " <separator />"
223 " <toolitem action='XAction' />"
224 " <toolitem action='YAction' />"
225 " <toolitem action='WidthAction' />"
226 " <toolitem action='LockAction' />"
227 " <toolitem action='HeightAction' />"
228 " <toolitem action='UnitsAction' />"
229 " <separator />"
230 " <toolitem action='transform_affect_label' />"
231 " <toolitem action='transform_stroke' />"
232 " <toolitem action='transform_corners' />"
233 " <toolitem action='transform_gradient' />"
234 " <toolitem action='transform_pattern' />"
235 " </toolbar>"
237 " <toolbar name='NodeToolbar'>"
238 " <toolitem action='NodeInsertAction' />"
239 " <toolitem action='NodeDeleteAction' />"
240 " <separator />"
241 " <toolitem action='NodeJoinAction' />"
242 " <toolitem action='NodeBreakAction' />"
243 " <separator />"
244 " <toolitem action='NodeJoinSegmentAction' />"
245 " <toolitem action='NodeDeleteSegmentAction' />"
246 " <separator />"
247 " <toolitem action='NodeCuspAction' />"
248 " <toolitem action='NodeSmoothAction' />"
249 " <toolitem action='NodeSymmetricAction' />"
250 " <separator />"
251 " <toolitem action='NodeLineAction' />"
252 " <toolitem action='NodeCurveAction' />"
253 " <separator />"
254 " <toolitem action='ObjectToPath' />"
255 " <toolitem action='StrokeToPath' />"
256 " <separator />"
257 " <toolitem action='NodeXAction' />"
258 " <toolitem action='NodeYAction' />"
259 " <toolitem action='NodeUnitsAction' />"
260 " <separator />"
261 " <toolitem action='ObjectEditClipPathAction' />"
262 " <toolitem action='ObjectEditMaskPathAction' />"
263 " <toolitem action='EditNextLPEParameterAction' />"
264 " <separator />"
265 " <toolitem action='NodesShowHandlesAction' />"
266 " <toolitem action='NodesShowHelperpath' />"
267 " </toolbar>"
269 " <toolbar name='TweakToolbar'>"
270 " <toolitem action='TweakWidthAction' />"
271 " <separator />"
272 " <toolitem action='TweakForceAction' />"
273 " <toolitem action='TweakPressureAction' />"
274 " <separator />"
275 " <toolitem action='TweakModeAction' />"
276 " <separator />"
277 " <toolitem action='TweakFidelityAction' />"
278 " <separator />"
279 " <toolitem action='TweakChannelsLabel' />"
280 " <toolitem action='TweakDoH' />"
281 " <toolitem action='TweakDoS' />"
282 " <toolitem action='TweakDoL' />"
283 " <toolitem action='TweakDoO' />"
284 " </toolbar>"
286 " <toolbar name='ZoomToolbar'>"
287 " <toolitem action='ZoomIn' />"
288 " <toolitem action='ZoomOut' />"
289 " <separator />"
290 " <toolitem action='Zoom1:0' />"
291 " <toolitem action='Zoom1:2' />"
292 " <toolitem action='Zoom2:1' />"
293 " <separator />"
294 " <toolitem action='ZoomSelection' />"
295 " <toolitem action='ZoomDrawing' />"
296 " <toolitem action='ZoomPage' />"
297 " <toolitem action='ZoomPageWidth' />"
298 " <separator />"
299 " <toolitem action='ZoomPrev' />"
300 " <toolitem action='ZoomNext' />"
301 " </toolbar>"
303 " <toolbar name='StarToolbar'>"
304 " <separator />"
305 " <toolitem action='StarStateAction' />"
306 " <separator />"
307 " <toolitem action='FlatAction' />"
308 " <separator />"
309 " <toolitem action='MagnitudeAction' />"
310 " <toolitem action='SpokeAction' />"
311 " <toolitem action='RoundednessAction' />"
312 " <toolitem action='RandomizationAction' />"
313 " <separator />"
314 " <toolitem action='StarResetAction' />"
315 " </toolbar>"
317 " <toolbar name='RectToolbar'>"
318 " <toolitem action='RectStateAction' />"
319 " <toolitem action='RectWidthAction' />"
320 " <toolitem action='RectHeightAction' />"
321 " <toolitem action='RadiusXAction' />"
322 " <toolitem action='RadiusYAction' />"
323 " <toolitem action='RectUnitsAction' />"
324 " <separator />"
325 " <toolitem action='RectResetAction' />"
326 " </toolbar>"
328 " <toolbar name='3DBoxToolbar'>"
329 " <toolitem action='3DBoxAngleXAction' />"
330 " <toolitem action='3DBoxVPXStateAction' />"
331 " <separator />"
332 " <toolitem action='3DBoxAngleYAction' />"
333 " <toolitem action='3DBoxVPYStateAction' />"
334 " <separator />"
335 " <toolitem action='3DBoxAngleZAction' />"
336 " <toolitem action='3DBoxVPZStateAction' />"
337 " </toolbar>"
339 " <toolbar name='SpiralToolbar'>"
340 " <toolitem action='SpiralStateAction' />"
341 " <toolitem action='SpiralRevolutionAction' />"
342 " <toolitem action='SpiralExpansionAction' />"
343 " <toolitem action='SpiralT0Action' />"
344 " <separator />"
345 " <toolitem action='SpiralResetAction' />"
346 " </toolbar>"
348 " <toolbar name='PenToolbar'>"
349 " </toolbar>"
351 " <toolbar name='PencilToolbar'>"
352 " </toolbar>"
354 " <toolbar name='CalligraphyToolbar'>"
355 " <separator />"
356 " <toolitem action='SetProfileAction'/>"
357 " <toolitem action='SaveDeleteProfileAction'/>"
358 " <separator />"
359 " <toolitem action='CalligraphyWidthAction' />"
360 " <toolitem action='PressureAction' />"
361 " <toolitem action='TraceAction' />"
362 " <toolitem action='ThinningAction' />"
363 " <separator />"
364 " <toolitem action='AngleAction' />"
365 " <toolitem action='TiltAction' />"
366 " <toolitem action='FixationAction' />"
367 " <separator />"
368 " <toolitem action='CapRoundingAction' />"
369 " <separator />"
370 " <toolitem action='TremorAction' />"
371 " <toolitem action='WiggleAction' />"
372 " <toolitem action='MassAction' />"
373 " <separator />"
374 " </toolbar>"
376 " <toolbar name='ArcToolbar'>"
377 " <toolitem action='ArcStateAction' />"
378 " <separator />"
379 " <toolitem action='ArcStartAction' />"
380 " <toolitem action='ArcEndAction' />"
381 " <separator />"
382 " <toolitem action='ArcOpenAction' />"
383 " <separator />"
384 " <toolitem action='ArcResetAction' />"
385 " <separator />"
386 " </toolbar>"
388 " <toolbar name='PaintbucketToolbar'>"
389 " <toolitem action='ChannelsAction' />"
390 " <separator />"
391 " <toolitem action='ThresholdAction' />"
392 " <separator />"
393 " <toolitem action='OffsetAction' />"
394 " <toolitem action='PaintbucketUnitsAction' />"
395 " <separator />"
396 " <toolitem action='AutoGapAction' />"
397 " <separator />"
398 " <toolitem action='PaintbucketResetAction' />"
399 " </toolbar>"
401 " <toolbar name='DropperToolbar'>"
402 " <toolitem action='DropperOpacityAction' />"
403 " <toolitem action='DropperPickAlphaAction' />"
404 " <toolitem action='DropperSetAlphaAction' />"
405 " </toolbar>"
407 " <toolbar name='ConnectorToolbar'>"
408 " <toolitem action='ConnectorAvoidAction' />"
409 " <toolitem action='ConnectorIgnoreAction' />"
410 " <toolitem action='ConnectorSpacingAction' />"
411 " <toolitem action='ConnectorGraphAction' />"
412 " <toolitem action='ConnectorLengthAction' />"
413 " <toolitem action='ConnectorDirectedAction' />"
414 " <toolitem action='ConnectorOverlapAction' />"
415 " </toolbar>"
417 "</ui>"
418 ;
420 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
422 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
424 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
425 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
427 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
428 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
430 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
431 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
433 /* Global text entry widgets necessary for update */
434 /* GtkWidget *dropper_rgb_entry,
435 *dropper_opacity_entry ; */
436 // should be made a private member once this is converted to class
438 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
439 connection->disconnect();
440 delete connection;
441 }
443 static void purge_repr_listener( GObject* obj, GObject* tbl )
444 {
445 (void)obj;
446 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
447 if (oldrepr) { // remove old listener
448 sp_repr_remove_listener_by_data(oldrepr, tbl);
449 Inkscape::GC::release(oldrepr);
450 oldrepr = 0;
451 g_object_set_data( tbl, "repr", NULL );
452 }
453 }
455 GtkWidget *
456 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
457 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
458 Inkscape::UI::View::View *view, GtkTooltips *tt)
459 {
460 SPAction *action = verb->get_action(view);
461 if (!action) return NULL;
463 SPAction *doubleclick_action;
464 if (doubleclick_verb)
465 doubleclick_action = doubleclick_verb->get_action(view);
466 else
467 doubleclick_action = NULL;
469 /* fixme: Handle sensitive/unsensitive */
470 /* fixme: Implement sp_button_new_from_action */
471 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
472 gtk_widget_show(b);
473 gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
475 return b;
476 }
478 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
479 Inkscape::UI::View::View *view, GtkTooltips *tt)
480 {
481 return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
482 }
484 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
485 Inkscape::UI::View::View *view, GtkTooltips *tt)
486 {
487 return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
488 }
491 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
492 {
493 SPAction* targetAction = SP_ACTION(user_data);
494 if ( targetAction ) {
495 sp_action_perform( targetAction, NULL );
496 }
497 }
499 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
500 {
501 if ( data ) {
502 GtkAction* act = GTK_ACTION(data);
503 gtk_action_set_sensitive( act, sensitive );
504 }
505 }
507 static SPActionEventVector action_event_vector = {
508 {NULL},
509 NULL,
510 NULL,
511 sp_action_action_set_sensitive,
512 NULL,
513 NULL
514 };
516 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
517 {
518 GtkAction* act = 0;
520 SPAction* targetAction = verb->get_action(view);
521 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
522 act = GTK_ACTION(inky);
523 gtk_action_set_sensitive( act, targetAction->sensitive );
525 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
527 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
528 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
530 return act;
531 }
533 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
534 {
535 Inkscape::UI::View::View *view = desktop;
536 gint verbsToUse[] = {
537 // disabled until we have icons for them:
538 //find
539 //SP_VERB_EDIT_TILE,
540 //SP_VERB_EDIT_UNTILE,
541 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
542 SP_VERB_DIALOG_DISPLAY,
543 SP_VERB_DIALOG_FILL_STROKE,
544 SP_VERB_DIALOG_NAMEDVIEW,
545 SP_VERB_DIALOG_TEXT,
546 SP_VERB_DIALOG_XML_EDITOR,
547 SP_VERB_EDIT_CLONE,
548 SP_VERB_EDIT_COPY,
549 SP_VERB_EDIT_CUT,
550 SP_VERB_EDIT_DUPLICATE,
551 SP_VERB_EDIT_PASTE,
552 SP_VERB_EDIT_REDO,
553 SP_VERB_EDIT_UNDO,
554 SP_VERB_EDIT_UNLINK_CLONE,
555 SP_VERB_FILE_EXPORT,
556 SP_VERB_FILE_IMPORT,
557 SP_VERB_FILE_NEW,
558 SP_VERB_FILE_OPEN,
559 SP_VERB_FILE_PRINT,
560 SP_VERB_FILE_SAVE,
561 SP_VERB_OBJECT_TO_CURVE,
562 SP_VERB_SELECTION_GROUP,
563 SP_VERB_SELECTION_OUTLINE,
564 SP_VERB_SELECTION_UNGROUP,
565 SP_VERB_ZOOM_1_1,
566 SP_VERB_ZOOM_1_2,
567 SP_VERB_ZOOM_2_1,
568 SP_VERB_ZOOM_DRAWING,
569 SP_VERB_ZOOM_IN,
570 SP_VERB_ZOOM_NEXT,
571 SP_VERB_ZOOM_OUT,
572 SP_VERB_ZOOM_PAGE,
573 SP_VERB_ZOOM_PAGE_WIDTH,
574 SP_VERB_ZOOM_PREV,
575 SP_VERB_ZOOM_SELECTION,
576 };
578 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
579 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
581 static std::map<SPDesktop*, GtkActionGroup*> groups;
582 GtkActionGroup* mainActions = 0;
583 if ( groups.find(desktop) != groups.end() ) {
584 mainActions = groups[desktop];
585 }
587 if ( !mainActions ) {
588 mainActions = gtk_action_group_new("main");
589 groups[desktop] = mainActions;
590 }
592 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
593 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
594 if ( verb ) {
595 if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
596 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
597 gtk_action_group_add_action( mainActions, act );
598 }
599 }
600 }
602 return mainActions;
603 }
606 GtkWidget *
607 sp_tool_toolbox_new()
608 {
609 GtkTooltips *tt = gtk_tooltips_new();
610 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
612 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
613 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
615 gtk_widget_set_sensitive(tb, FALSE);
617 GtkWidget *hb = gtk_handle_box_new();
618 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
619 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
620 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
622 gtk_container_add(GTK_CONTAINER(hb), tb);
623 gtk_widget_show(GTK_WIDGET(tb));
625 sigc::connection* conn = new sigc::connection;
626 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
628 return hb;
629 }
631 static void
632 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
633 {
634 g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
635 gtk_widget_queue_resize(child);
636 }
638 static void
639 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
640 {
641 g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
642 gtk_widget_queue_resize(child);
643 }
645 GtkWidget *
646 sp_aux_toolbox_new()
647 {
648 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
650 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
652 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
654 gtk_widget_set_sensitive(tb, FALSE);
656 GtkWidget *hb = gtk_handle_box_new();
657 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
658 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
659 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
661 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
662 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
664 gtk_container_add(GTK_CONTAINER(hb), tb);
665 gtk_widget_show(GTK_WIDGET(tb));
667 sigc::connection* conn = new sigc::connection;
668 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
670 return hb;
671 }
673 //####################################
674 //# Commands Bar
675 //####################################
677 GtkWidget *
678 sp_commands_toolbox_new()
679 {
680 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
682 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
684 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
685 gtk_widget_set_sensitive(tb, FALSE);
687 GtkWidget *hb = gtk_handle_box_new();
688 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
689 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
690 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
692 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
693 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
695 gtk_container_add(GTK_CONTAINER(hb), tb);
696 gtk_widget_show(GTK_WIDGET(tb));
698 sigc::connection* conn = new sigc::connection;
699 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
701 return hb;
702 }
704 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
705 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
706 gchar const *path, gchar const *data, gdouble def,
707 GtkWidget *focusTarget,
708 GtkWidget *us,
709 GObject *dataKludge,
710 gboolean altx, gchar const *altx_mark,
711 gdouble lower, gdouble upper, gdouble step, gdouble page,
712 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
713 void (*callback)(GtkAdjustment *, GObject *),
714 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
715 {
716 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
717 lower, upper, step, page, page ) );
718 if (us) {
719 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
720 }
722 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
724 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
725 if ( shortLabel ) {
726 g_object_set( act, "short_label", shortLabel, NULL );
727 }
729 if ( (descrCount > 0) && descrLabels && descrValues ) {
730 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
731 }
733 if ( focusTarget ) {
734 ege_adjustment_action_set_focuswidget( act, focusTarget );
735 }
737 if ( altx && altx_mark ) {
738 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
739 }
741 if ( dataKludge ) {
742 g_object_set_data( dataKludge, data, adj );
743 }
745 // Using a cast just to make sure we pass in the right kind of function pointer
746 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
748 return act;
749 }
752 //####################################
753 //# node editing callbacks
754 //####################################
756 /**
757 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
758 */
759 static ShapeEditor *get_current_shape_editor()
760 {
761 if (!SP_ACTIVE_DESKTOP) {
762 return NULL;
763 }
765 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
767 if (!SP_IS_NODE_CONTEXT(event_context)) {
768 return NULL;
769 }
771 return SP_NODE_CONTEXT(event_context)->shape_editor;
772 }
775 void
776 sp_node_path_edit_add(void)
777 {
778 ShapeEditor *shape_editor = get_current_shape_editor();
779 if (shape_editor) shape_editor->add_node();
780 }
782 void
783 sp_node_path_edit_delete(void)
784 {
785 ShapeEditor *shape_editor = get_current_shape_editor();
786 if (shape_editor) shape_editor->delete_nodes_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 g_object_set( act, "short_label", _("H"), NULL );
3274 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3275 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3276 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3277 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3278 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3279 g_object_set_data( holder, "tweak_doh", act);
3280 }
3281 {
3282 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3283 _("Saturation"),
3284 _("In color mode, act on objects' saturation"),
3285 NULL,
3286 Inkscape::ICON_SIZE_DECORATION );
3287 g_object_set( act, "short_label", _("S"), NULL );
3288 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3289 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3290 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3291 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3292 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3293 g_object_set_data( holder, "tweak_dos", act );
3294 }
3295 {
3296 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3297 _("Lightness"),
3298 _("In color mode, act on objects' lightness"),
3299 NULL,
3300 Inkscape::ICON_SIZE_DECORATION );
3301 g_object_set( act, "short_label", _("L"), NULL );
3302 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3303 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3304 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3305 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3306 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3307 g_object_set_data( holder, "tweak_dol", act );
3308 }
3309 {
3310 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3311 _("Opacity"),
3312 _("In color mode, act on objects' opacity"),
3313 NULL,
3314 Inkscape::ICON_SIZE_DECORATION );
3315 g_object_set( act, "short_label", _("O"), NULL );
3316 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3317 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3318 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3319 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3320 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3321 g_object_set_data( holder, "tweak_doo", act );
3322 }
3324 { /* Fidelity */
3325 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3326 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3327 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3328 _("Fidelity"), _("Fidelity:"),
3329 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3330 "tools.tweak", "fidelity", 50,
3331 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3332 1, 100, 1.0, 10.0,
3333 labels, values, G_N_ELEMENTS(labels),
3334 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3335 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3336 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3337 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3338 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3339 g_object_set_data( holder, "tweak_fidelity", eact );
3340 }
3343 /* Use Pressure button */
3344 {
3345 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3346 _("Pressure"),
3347 _("Use the pressure of the input device to alter the force of tweak action"),
3348 "use_pressure",
3349 Inkscape::ICON_SIZE_DECORATION );
3350 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3351 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3352 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3353 }
3355 }
3358 //########################
3359 //## Calligraphy ##
3360 //########################
3361 static void update_presets_list(GObject *dataKludge ){
3362 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3363 if (sel) {
3364 ege_select_one_action_set_active(sel, 0 );
3365 }
3366 }
3368 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3369 {
3370 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3371 update_presets_list(tbl);
3372 }
3374 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3375 {
3376 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3377 update_presets_list(tbl);
3378 }
3380 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3381 {
3382 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3383 update_presets_list(tbl);
3384 }
3386 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3387 {
3388 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3389 update_presets_list(tbl);
3390 }
3392 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3393 {
3394 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3395 update_presets_list(tbl);
3396 }
3398 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3399 {
3400 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3401 update_presets_list(tbl);
3402 }
3404 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3405 {
3406 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3407 update_presets_list(tbl);
3408 }
3410 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3411 {
3412 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3413 update_presets_list(tbl);
3414 }
3416 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3417 {
3418 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3419 update_presets_list(tbl);
3420 }
3422 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
3423 {
3424 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3425 update_presets_list(tbl);
3426 }
3428 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
3429 {
3430 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3431 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3432 update_presets_list(tbl);
3433 if (calligraphy_angle )
3434 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3435 }
3438 #define PROFILE_FLOAT_SIZE 7
3439 #define PROFILE_INT_SIZE 4
3440 struct ProfileFloatElement {
3441 char const *name;
3442 double def;
3443 double min;
3444 double max;
3445 };
3446 struct ProfileIntElement {
3447 char const *name;
3448 int def;
3449 int min;
3450 int max;
3451 };
3455 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3456 {"mass",0.02, 0.0, 1.0},
3457 {"wiggle",0.0, 0.0, 1.0},
3458 {"angle",30.0, -90.0, 90.0},
3459 {"thinning",0.1, -1.0, 1.0},
3460 {"tremor",0.0, 0.0, 1.0},
3461 {"flatness",0.9, 0.0, 1.0},
3462 {"cap_rounding",0.0, 0.0, 5.0}
3463 };
3464 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3465 {"width",15, 1, 100},
3466 {"usepressure",1,0,1},
3467 {"tracebackground",0,0,1},
3468 {"usetilt",1,0,1},
3469 };
3473 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3474 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3475 if (! desktop) return;
3477 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3478 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3479 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3481 unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3482 gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3483 gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);
3485 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3486 ProfileFloatElement const &pe = f_profile[i];
3487 double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3488 prefs_set_double_attribute(pref_path,pe.name,v);
3489 }
3490 for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3491 ProfileIntElement const &pe = i_profile[i];
3492 int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3493 prefs_set_int_attribute(pref_path,pe.name,v);
3494 }
3495 prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3497 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3498 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3499 GtkTreeIter iter;
3500 gtk_list_store_append( model, &iter );
3501 gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3503 free(profile_id);
3504 free(pref_path);
3506 ege_select_one_action_set_active(selector, new_index);
3507 }
3510 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3512 gint preset_index = ege_select_one_action_get_active( act );
3513 gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3515 if ( profile_name) {
3516 g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3517 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3518 ProfileFloatElement const &pe = f_profile[i];
3519 double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3520 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3521 if ( adj ) {
3522 gtk_adjustment_set_value(adj, v);
3523 }
3524 }
3525 for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3526 ProfileIntElement const &pe = i_profile[i];
3527 int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3528 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3529 if ( toggle ) {
3530 gtk_toggle_action_set_active(toggle, v);
3531 } else printf("No toggle");
3532 }
3533 free(profile_name);
3534 g_object_set_data(dataKludge, "profile_selector",act); //restore selector visibility
3535 }
3537 }
3540 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3541 {
3542 {
3543 EgeAdjustmentAction* calligraphy_angle = 0;
3545 {
3546 /* Width */
3547 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3548 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3549 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3550 _("Pen Width"), _("Width:"),
3551 _("The width of the calligraphic pen (relative to the visible canvas area)"),
3552 "tools.calligraphic", "width", 15,
3553 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3554 1, 100, 1.0, 10.0,
3555 labels, values, G_N_ELEMENTS(labels),
3556 sp_ddc_width_value_changed, 0.01, 0, 100 );
3557 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3558 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3559 }
3561 {
3562 /* Thinning */
3563 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3564 gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3565 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3566 _("Stroke Thinning"), _("Thinning:"),
3567 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3568 "tools.calligraphic", "thinning", 0.1,
3569 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3570 -1.0, 1.0, 0.01, 0.1,
3571 labels, values, G_N_ELEMENTS(labels),
3572 sp_ddc_velthin_value_changed, 0.01, 2);
3573 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3574 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3575 }
3577 {
3578 /* Angle */
3579 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3580 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3581 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3582 _("Pen Angle"), _("Angle:"),
3583 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3584 "tools.calligraphic", "angle", 30,
3585 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3586 -90.0, 90.0, 1.0, 10.0,
3587 labels, values, G_N_ELEMENTS(labels),
3588 sp_ddc_angle_value_changed, 1, 0 );
3589 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3590 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3591 g_object_set_data( holder, "angle", eact );
3592 calligraphy_angle = eact;
3593 }
3595 {
3596 /* Fixation */
3597 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3598 gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3599 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3600 _("Fixation"), _("Fixation:"),
3601 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3602 "tools.calligraphic", "flatness", 0.9,
3603 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3604 0.0, 1.0, 0.01, 0.1,
3605 labels, values, G_N_ELEMENTS(labels),
3606 sp_ddc_flatness_value_changed, 0.01, 2 );
3607 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3608 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3609 }
3611 {
3612 /* Cap Rounding */
3613 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3614 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3615 // TRANSLATORS: "cap" means "end" (both start and finish) here
3616 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3617 _("Cap rounding"), _("Caps:"),
3618 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3619 "tools.calligraphic", "cap_rounding", 0.0,
3620 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3621 0.0, 5.0, 0.01, 0.1,
3622 labels, values, G_N_ELEMENTS(labels),
3623 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3624 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3625 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3626 }
3628 {
3629 /* Tremor */
3630 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3631 gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3632 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3633 _("Stroke Tremor"), _("Tremor:"),
3634 _("Increase to make strokes rugged and trembling"),
3635 "tools.calligraphic", "tremor", 0.0,
3636 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3637 0.0, 1.0, 0.01, 0.1,
3638 labels, values, G_N_ELEMENTS(labels),
3639 sp_ddc_tremor_value_changed, 0.01, 2 );
3641 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3642 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3643 }
3645 {
3646 /* Wiggle */
3647 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3648 gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3649 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3650 _("Pen Wiggle"), _("Wiggle:"),
3651 _("Increase to make the pen waver and wiggle"),
3652 "tools.calligraphic", "wiggle", 0.0,
3653 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3654 0.0, 1.0, 0.01, 0.1,
3655 labels, values, G_N_ELEMENTS(labels),
3656 sp_ddc_wiggle_value_changed, 0.01, 2 );
3657 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3658 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3659 }
3661 {
3662 /* Mass */
3663 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3664 gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3665 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3666 _("Pen Mass"), _("Mass:"),
3667 _("Increase to make the pen drag behind, as if slowed by inertia"),
3668 "tools.calligraphic", "mass", 0.02,
3669 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3670 0.0, 1.0, 0.01, 0.1,
3671 labels, values, G_N_ELEMENTS(labels),
3672 sp_ddc_mass_value_changed, 0.01, 2 );
3673 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3674 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3675 }
3678 /* Trace Background button */
3679 {
3680 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3681 _("Trace Background"),
3682 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3683 "trace_background",
3684 Inkscape::ICON_SIZE_DECORATION );
3685 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3686 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
3687 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3688 g_object_set_data( holder, "tracebackground", act );
3689 }
3691 /* Use Pressure button */
3692 {
3693 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3694 _("Pressure"),
3695 _("Use the pressure of the input device to alter the width of the pen"),
3696 "use_pressure",
3697 Inkscape::ICON_SIZE_DECORATION );
3698 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3699 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
3700 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3701 g_object_set_data( holder, "usepressure", act );
3702 }
3704 /* Use Tilt button */
3705 {
3706 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3707 _("Tilt"),
3708 _("Use the tilt of the input device to alter the angle of the pen's nib"),
3709 "use_tilt",
3710 Inkscape::ICON_SIZE_DECORATION );
3711 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3712 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
3713 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3714 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3715 g_object_set_data( holder, "usetilt", act );
3716 }
3718 /*calligraphic profile */
3719 {
3720 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3721 gchar *pref_path;
3724 GtkTreeIter iter;
3725 gtk_list_store_append( model, &iter );
3726 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3728 //TODO: switch back to prefs API
3729 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3730 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3731 int ii=1;
3732 while (child_repr) {
3733 GtkTreeIter iter;
3734 char *preset_name = (char *) child_repr->attribute("name");
3735 gtk_list_store_append( model, &iter );
3736 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
3737 child_repr = sp_repr_next(child_repr);
3738 }
3740 pref_path = NULL;
3741 EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , ("Change calligraphic profile"), NULL, GTK_TREE_MODEL(model) );
3742 ege_select_one_action_set_appearance( act1, "compact" );
3743 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
3744 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3745 g_object_set_data( holder, "profile_selector", act1 );
3747 }
3749 /*Save or delete calligraphic profile */
3750 {
3751 GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
3752 _("Defaults"),
3753 _("Save current settings as new profile"),
3754 GTK_STOCK_SAVE );
3755 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
3758 gtk_action_group_add_action( mainActions, act );
3759 gtk_action_set_sensitive( act, TRUE );
3760 g_object_set_data( holder, "profile_save_delete", act );
3761 }
3762 }
3763 }
3766 //########################
3767 //## Circle / Arc ##
3768 //########################
3770 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3771 {
3772 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3773 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3775 if (v1 == 0 && v2 == 0) {
3776 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3777 gtk_action_set_sensitive( ocb, FALSE );
3778 gtk_action_set_sensitive( make_whole, FALSE );
3779 }
3780 } else {
3781 gtk_action_set_sensitive( ocb, TRUE );
3782 gtk_action_set_sensitive( make_whole, TRUE );
3783 }
3784 }
3786 static void
3787 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3788 {
3789 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3791 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3792 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3793 }
3795 // quit if run by the attr_changed listener
3796 if (g_object_get_data( tbl, "freeze" )) {
3797 return;
3798 }
3800 // in turn, prevent listener from responding
3801 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3803 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3805 bool modmade = false;
3806 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3807 items != NULL;
3808 items = items->next)
3809 {
3810 SPItem *item = SP_ITEM(items->data);
3812 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3814 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3815 SPArc *arc = SP_ARC(item);
3817 if (!strcmp(value_name, "start"))
3818 ge->start = (adj->value * M_PI)/ 180;
3819 else
3820 ge->end = (adj->value * M_PI)/ 180;
3822 sp_genericellipse_normalize(ge);
3823 ((SPObject *)arc)->updateRepr();
3824 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3826 modmade = true;
3827 }
3828 }
3830 g_free(namespaced_name);
3832 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3834 sp_arctb_sensitivize( tbl, adj->value, other->value );
3836 if (modmade) {
3837 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3838 _("Arc: Change start/end"));
3839 }
3841 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3842 }
3845 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
3846 {
3847 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
3848 }
3850 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3851 {
3852 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
3853 }
3855 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3856 {
3857 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3858 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3859 if ( ege_select_one_action_get_active( act ) != 0 ) {
3860 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3861 } else {
3862 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3863 }
3864 }
3866 // quit if run by the attr_changed listener
3867 if (g_object_get_data( tbl, "freeze" )) {
3868 return;
3869 }
3871 // in turn, prevent listener from responding
3872 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3874 bool modmade = false;
3876 if ( ege_select_one_action_get_active(act) != 0 ) {
3877 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3878 items != NULL;
3879 items = items->next)
3880 {
3881 if (SP_IS_ARC((SPItem *) items->data)) {
3882 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3883 repr->setAttribute("sodipodi:open", "true");
3884 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3885 modmade = true;
3886 }
3887 }
3888 } else {
3889 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3890 items != NULL;
3891 items = items->next)
3892 {
3893 if (SP_IS_ARC((SPItem *) items->data)) {
3894 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3895 repr->setAttribute("sodipodi:open", NULL);
3896 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3897 modmade = true;
3898 }
3899 }
3900 }
3902 if (modmade) {
3903 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3904 _("Arc: Change open/closed"));
3905 }
3907 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3908 }
3910 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3911 {
3912 GtkAdjustment *adj;
3913 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3914 gtk_adjustment_set_value(adj, 0.0);
3915 gtk_adjustment_value_changed(adj);
3917 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3918 gtk_adjustment_set_value(adj, 0.0);
3919 gtk_adjustment_value_changed(adj);
3921 spinbutton_defocus( GTK_OBJECT(obj) );
3922 }
3924 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3925 gchar const */*old_value*/, gchar const */*new_value*/,
3926 bool /*is_interactive*/, gpointer data)
3927 {
3928 GObject *tbl = G_OBJECT(data);
3930 // quit if run by the _changed callbacks
3931 if (g_object_get_data( tbl, "freeze" )) {
3932 return;
3933 }
3935 // in turn, prevent callbacks from responding
3936 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3938 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3939 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3941 GtkAdjustment *adj1,*adj2;
3942 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3943 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3944 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3945 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3947 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3949 char const *openstr = NULL;
3950 openstr = repr->attribute("sodipodi:open");
3951 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3953 if (openstr) {
3954 ege_select_one_action_set_active( ocb, 1 );
3955 } else {
3956 ege_select_one_action_set_active( ocb, 0 );
3957 }
3959 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3960 }
3962 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3963 NULL, /* child_added */
3964 NULL, /* child_removed */
3965 arc_tb_event_attr_changed,
3966 NULL, /* content_changed */
3967 NULL /* order_changed */
3968 };
3971 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3972 {
3973 int n_selected = 0;
3974 Inkscape::XML::Node *repr = NULL;
3976 purge_repr_listener( tbl, tbl );
3978 for (GSList const *items = selection->itemList();
3979 items != NULL;
3980 items = items->next)
3981 {
3982 if (SP_IS_ARC((SPItem *) items->data)) {
3983 n_selected++;
3984 repr = SP_OBJECT_REPR((SPItem *) items->data);
3985 }
3986 }
3988 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3990 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3991 if (n_selected == 0) {
3992 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3993 } else if (n_selected == 1) {
3994 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3995 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3997 if (repr) {
3998 g_object_set_data( tbl, "repr", repr );
3999 Inkscape::GC::anchor(repr);
4000 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4001 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4002 }
4003 } else {
4004 // FIXME: implement averaging of all parameters for multiple selected
4005 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4006 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4007 sp_arctb_sensitivize( tbl, 1, 0 );
4008 }
4009 }
4012 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4013 {
4014 EgeAdjustmentAction* eact = 0;
4017 {
4018 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4019 ege_output_action_set_use_markup( act, TRUE );
4020 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4021 g_object_set_data( holder, "mode_action", act );
4022 }
4024 /* Start */
4025 {
4026 eact = create_adjustment_action( "ArcStartAction",
4027 _("Start"), _("Start:"),
4028 _("The angle (in degrees) from the horizontal to the arc's start point"),
4029 "tools.shapes.arc", "start", 0.0,
4030 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4031 -360.0, 360.0, 1.0, 10.0,
4032 0, 0, 0,
4033 sp_arctb_start_value_changed);
4034 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4035 }
4037 /* End */
4038 {
4039 eact = create_adjustment_action( "ArcEndAction",
4040 _("End"), _("End:"),
4041 _("The angle (in degrees) from the horizontal to the arc's end point"),
4042 "tools.shapes.arc", "end", 0.0,
4043 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4044 -360.0, 360.0, 1.0, 10.0,
4045 0, 0, 0,
4046 sp_arctb_end_value_changed);
4047 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4048 }
4050 /* Segments / Pie checkbox */
4051 {
4052 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4054 GtkTreeIter iter;
4055 gtk_list_store_append( model, &iter );
4056 gtk_list_store_set( model, &iter,
4057 0, _("Closed arc"),
4058 1, _("Switch to segment (closed shape with two radii)"),
4059 2, "circle_closed_arc",
4060 -1 );
4062 gtk_list_store_append( model, &iter );
4063 gtk_list_store_set( model, &iter,
4064 0, _("Open Arc"),
4065 1, _("Switch to arc (unclosed shape)"),
4066 2, "circle_open_arc",
4067 -1 );
4069 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4070 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4071 g_object_set_data( holder, "open_action", act );
4073 ege_select_one_action_set_appearance( act, "full" );
4074 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4075 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4076 ege_select_one_action_set_icon_column( act, 2 );
4077 ege_select_one_action_set_tooltip_column( act, 1 );
4079 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4080 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4081 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4082 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4083 }
4085 /* Make Whole */
4086 {
4087 InkAction* inky = ink_action_new( "ArcResetAction",
4088 _("Make whole"),
4089 _("Make the shape a whole ellipse, not arc or segment"),
4090 "reset_circle",
4091 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4092 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4093 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4094 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4095 g_object_set_data( holder, "make_whole", inky );
4096 }
4098 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4099 // sensitivize make whole and open checkbox
4100 {
4101 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4102 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4103 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4104 }
4107 sigc::connection *connection = new sigc::connection(
4108 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4109 );
4110 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4111 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4112 }
4117 // toggle button callbacks and updaters
4119 //########################
4120 //## Dropper ##
4121 //########################
4123 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4124 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4125 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4126 if ( set_action ) {
4127 if ( gtk_toggle_action_get_active( act ) ) {
4128 gtk_action_set_sensitive( set_action, TRUE );
4129 } else {
4130 gtk_action_set_sensitive( set_action, FALSE );
4131 }
4132 }
4134 spinbutton_defocus(GTK_OBJECT(tbl));
4135 }
4137 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4138 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4139 spinbutton_defocus(GTK_OBJECT(tbl));
4140 }
4143 /**
4144 * Dropper auxiliary toolbar construction and setup.
4145 *
4146 * TODO: Would like to add swatch of current color.
4147 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4148 * can drag and drop places. Will provide a nice mixing palette.
4149 */
4150 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4151 {
4152 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4154 {
4155 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4156 ege_output_action_set_use_markup( act, TRUE );
4157 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4158 }
4160 {
4161 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4162 _("Pick opacity"),
4163 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4164 NULL,
4165 Inkscape::ICON_SIZE_DECORATION );
4166 g_object_set( act, "short_label", _("Pick"), NULL );
4167 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4168 g_object_set_data( holder, "pick_action", act );
4169 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4170 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4171 }
4173 {
4174 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4175 _("Assign opacity"),
4176 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4177 NULL,
4178 Inkscape::ICON_SIZE_DECORATION );
4179 g_object_set( act, "short_label", _("Assign"), NULL );
4180 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4181 g_object_set_data( holder, "set_action", act );
4182 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4183 // make sure it's disabled if we're not picking alpha
4184 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4185 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4186 }
4187 }
4190 //########################
4191 //## Text Toolbox ##
4192 //########################
4193 /*
4194 static void
4195 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4196 {
4197 //Call back for letter sizing spinbutton
4198 }
4200 static void
4201 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4202 {
4203 //Call back for line height spinbutton
4204 }
4206 static void
4207 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4208 {
4209 //Call back for horizontal kerning spinbutton
4210 }
4212 static void
4213 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4214 {
4215 //Call back for vertical kerning spinbutton
4216 }
4218 static void
4219 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4220 {
4221 //Call back for letter rotation spinbutton
4222 }*/
4224 namespace {
4226 bool popdown_visible = false;
4227 bool popdown_hasfocus = false;
4229 void
4230 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4231 {
4232 SPStyle *query =
4233 sp_style_new (SP_ACTIVE_DOCUMENT);
4235 int result_fontspec =
4236 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4238 int result_family =
4239 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4241 int result_style =
4242 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4244 int result_numbers =
4245 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4247 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4249 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4250 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4251 {
4252 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4254 if (repr)
4255 {
4256 sp_style_read_from_repr (query, repr);
4257 }
4258 else
4259 {
4260 return;
4261 }
4262 }
4264 if (query->text)
4265 {
4266 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4267 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4268 gtk_entry_set_text (GTK_ENTRY (entry), "");
4270 } else if (query->text->font_specification.value || query->text->font_family.value) {
4272 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4274 // Get the font that corresponds
4275 Glib::ustring familyName;
4277 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4278 if (font) {
4279 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4280 font->Unref();
4281 font = NULL;
4282 }
4284 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4286 Gtk::TreePath path;
4287 try {
4288 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4289 } catch (...) {
4290 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4291 return;
4292 }
4294 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4295 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4297 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4299 gtk_tree_selection_select_path (tselection, path.gobj());
4300 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4302 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4303 }
4305 //Size
4306 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4307 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4308 g_object_set_data (tbl, "size-block", gpointer(1));
4309 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4310 g_object_set_data (tbl, "size-block", gpointer(0));
4311 free (str);
4313 //Anchor
4314 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4315 {
4316 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4317 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4318 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4319 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4320 }
4321 else
4322 {
4323 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4324 {
4325 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4326 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4327 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4328 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4329 }
4330 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4331 {
4332 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4333 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4334 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4335 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4336 }
4337 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4338 {
4339 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4340 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4341 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4342 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4343 }
4344 }
4346 //Style
4347 {
4348 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4350 gboolean active = gtk_toggle_button_get_active (button);
4351 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4353 if (active != check)
4354 {
4355 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4356 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4357 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4358 }
4359 }
4361 {
4362 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4364 gboolean active = gtk_toggle_button_get_active (button);
4365 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4367 if (active != check)
4368 {
4369 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4370 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4371 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4372 }
4373 }
4375 //Orientation
4376 //locking both buttons, changing one affect all group (both)
4377 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4378 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4380 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4381 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4383 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4384 {
4385 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4386 }
4387 else
4388 {
4389 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4390 }
4391 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4392 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4393 }
4395 sp_style_unref(query);
4396 }
4398 void
4399 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4400 {
4401 sp_text_toolbox_selection_changed (selection, tbl);
4402 }
4404 void
4405 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4406 {
4407 sp_text_toolbox_selection_changed (NULL, tbl);
4408 }
4410 void
4411 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
4412 GObject *tbl)
4413 {
4414 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4415 GtkTreeModel *model = 0;
4416 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4417 GtkTreeIter iter;
4418 char *family = 0;
4420 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4421 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4423 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4424 return;
4425 }
4427 gtk_tree_model_get (model, &iter, 0, &family, -1);
4429 if (g_object_get_data (G_OBJECT (selection), "block"))
4430 {
4431 gtk_entry_set_text (GTK_ENTRY (entry), family);
4432 return;
4433 }
4435 gtk_entry_set_text (GTK_ENTRY (entry), family);
4437 SPStyle *query =
4438 sp_style_new (SP_ACTIVE_DOCUMENT);
4440 int result_fontspec =
4441 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4443 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4445 SPCSSAttr *css = sp_repr_css_attr_new ();
4448 // First try to get the font spec from the stored value
4449 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4451 if (fontSpec.empty()) {
4452 // Construct a new font specification if it does not yet exist
4453 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4454 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4455 fontFromStyle->Unref();
4456 }
4458 if (!fontSpec.empty()) {
4459 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4460 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4461 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4462 if (font) {
4463 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4465 // Set all the these just in case they were altered when finding the best
4466 // match for the new family and old style...
4468 gchar c[256];
4470 font->Family(c, 256);
4471 sp_repr_css_set_property (css, "font-family", c);
4473 font->Attribute( "weight", c, 256);
4474 sp_repr_css_set_property (css, "font-weight", c);
4476 font->Attribute("style", c, 256);
4477 sp_repr_css_set_property (css, "font-style", c);
4479 font->Attribute("stretch", c, 256);
4480 sp_repr_css_set_property (css, "font-stretch", c);
4482 font->Attribute("variant", c, 256);
4483 sp_repr_css_set_property (css, "font-variant", c);
4485 font->Unref();
4486 }
4487 }
4488 }
4490 // If querying returned nothing, set the default style of the tool (for new texts)
4491 if (result_fontspec == QUERY_STYLE_NOTHING)
4492 {
4493 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4494 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4495 }
4496 else
4497 {
4498 sp_desktop_set_style (desktop, css, true, true);
4499 }
4501 sp_style_unref(query);
4503 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4504 _("Text: Change font family"));
4505 sp_repr_css_attr_unref (css);
4506 free (family);
4507 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4509 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4510 }
4512 void
4513 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
4514 GObject *tbl)
4515 {
4516 const char *family = gtk_entry_get_text (entry);
4518 try {
4519 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4520 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4521 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4522 gtk_tree_selection_select_path (selection, path.gobj());
4523 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4524 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4525 } catch (...) {
4526 if (family && strlen (family))
4527 {
4528 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4529 }
4530 }
4531 }
4533 void
4534 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
4535 gpointer data)
4536 {
4537 if (g_object_get_data (G_OBJECT (button), "block")) return;
4538 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4539 int prop = GPOINTER_TO_INT(data);
4541 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4542 SPCSSAttr *css = sp_repr_css_attr_new ();
4544 switch (prop)
4545 {
4546 case 0:
4547 {
4548 sp_repr_css_set_property (css, "text-anchor", "start");
4549 sp_repr_css_set_property (css, "text-align", "start");
4550 break;
4551 }
4552 case 1:
4553 {
4554 sp_repr_css_set_property (css, "text-anchor", "middle");
4555 sp_repr_css_set_property (css, "text-align", "center");
4556 break;
4557 }
4559 case 2:
4560 {
4561 sp_repr_css_set_property (css, "text-anchor", "end");
4562 sp_repr_css_set_property (css, "text-align", "end");
4563 break;
4564 }
4566 case 3:
4567 {
4568 sp_repr_css_set_property (css, "text-anchor", "start");
4569 sp_repr_css_set_property (css, "text-align", "justify");
4570 break;
4571 }
4572 }
4574 SPStyle *query =
4575 sp_style_new (SP_ACTIVE_DOCUMENT);
4576 int result_numbers =
4577 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4579 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4580 if (result_numbers == QUERY_STYLE_NOTHING)
4581 {
4582 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4583 }
4585 sp_style_unref(query);
4587 sp_desktop_set_style (desktop, css, true, true);
4588 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4589 _("Text: Change alignment"));
4590 sp_repr_css_attr_unref (css);
4592 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4593 }
4595 void
4596 sp_text_toolbox_style_toggled (GtkToggleButton *button,
4597 gpointer data)
4598 {
4599 if (g_object_get_data (G_OBJECT (button), "block")) return;
4601 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4602 SPCSSAttr *css = sp_repr_css_attr_new ();
4603 int prop = GPOINTER_TO_INT(data);
4604 bool active = gtk_toggle_button_get_active (button);
4606 SPStyle *query =
4607 sp_style_new (SP_ACTIVE_DOCUMENT);
4609 int result_fontspec =
4610 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4612 int result_family =
4613 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4615 int result_style =
4616 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4618 int result_numbers =
4619 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4621 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4622 Glib::ustring newFontSpec = "";
4624 if (fontSpec.empty()) {
4625 // Construct a new font specification if it does not yet exist
4626 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4627 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4628 fontFromStyle->Unref();
4629 }
4631 switch (prop)
4632 {
4633 case 0:
4634 {
4635 if (!fontSpec.empty()) {
4636 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4637 }
4638 if (fontSpec != newFontSpec) {
4639 // Don't even set the bold if the font didn't exist on the system
4640 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4641 }
4642 break;
4643 }
4645 case 1:
4646 {
4647 if (!fontSpec.empty()) {
4648 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4649 }
4650 if (fontSpec != newFontSpec) {
4651 // Don't even set the italic if the font didn't exist on the system
4652 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4653 }
4654 break;
4655 }
4656 }
4658 if (!newFontSpec.empty()) {
4659 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4660 }
4662 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4663 if (result_fontspec == QUERY_STYLE_NOTHING)
4664 {
4665 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4666 }
4668 sp_style_unref(query);
4670 sp_desktop_set_style (desktop, css, true, true);
4671 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4672 _("Text: Change font style"));
4673 sp_repr_css_attr_unref (css);
4675 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4676 }
4678 void
4679 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
4680 gpointer data)
4681 {
4682 if (g_object_get_data (G_OBJECT (button), "block")) {
4683 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4684 return;
4685 }
4687 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4688 SPCSSAttr *css = sp_repr_css_attr_new ();
4689 int prop = GPOINTER_TO_INT(data);
4691 switch (prop)
4692 {
4693 case 0:
4694 {
4695 sp_repr_css_set_property (css, "writing-mode", "lr");
4696 break;
4697 }
4699 case 1:
4700 {
4701 sp_repr_css_set_property (css, "writing-mode", "tb");
4702 break;
4703 }
4704 }
4706 SPStyle *query =
4707 sp_style_new (SP_ACTIVE_DOCUMENT);
4708 int result_numbers =
4709 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4711 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4712 if (result_numbers == QUERY_STYLE_NOTHING)
4713 {
4714 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4715 }
4717 sp_desktop_set_style (desktop, css, true, true);
4718 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4719 _("Text: Change orientation"));
4720 sp_repr_css_attr_unref (css);
4722 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4723 }
4725 gboolean
4726 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4727 {
4728 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4729 if (!desktop) return FALSE;
4731 switch (get_group0_keyval (event)) {
4732 case GDK_Escape: // defocus
4733 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4734 sp_text_toolbox_selection_changed (NULL, tbl); // update
4735 return TRUE; // I consumed the event
4736 break;
4737 }
4738 return FALSE;
4739 }
4741 gboolean
4742 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4743 {
4744 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4745 if (!desktop) return FALSE;
4747 switch (get_group0_keyval (event)) {
4748 case GDK_KP_Enter:
4749 case GDK_Return:
4750 case GDK_Escape: // defocus
4751 gtk_widget_hide (w);
4752 popdown_visible = false;
4753 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4754 return TRUE; // I consumed the event
4755 break;
4756 case GDK_w:
4757 case GDK_W:
4758 if (event->state & GDK_CONTROL_MASK) {
4759 gtk_widget_hide (w);
4760 popdown_visible = false;
4761 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4762 return TRUE; // I consumed the event
4763 }
4764 break;
4765 }
4766 return FALSE;
4767 }
4770 void
4771 sp_text_toolbox_size_changed (GtkComboBox *cbox,
4772 GObject *tbl)
4773 {
4774 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4776 if (g_object_get_data (tbl, "size-block")) return;
4778 // If this is not from selecting a size in the list (in which case get_active will give the
4779 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4780 // process this event. This fixes GTK's stupid insistence on sending an activate change every
4781 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4782 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4783 return;
4785 gchar *endptr;
4786 gdouble value = -1;
4787 char *text = gtk_combo_box_get_active_text (cbox);
4788 if (text) {
4789 value = g_strtod (text, &endptr);
4790 if (endptr == text) // conversion failed, non-numeric input
4791 value = -1;
4792 free (text);
4793 }
4794 if (value <= 0) {
4795 return; // could not parse value
4796 }
4798 SPCSSAttr *css = sp_repr_css_attr_new ();
4799 Inkscape::CSSOStringStream osfs;
4800 osfs << value;
4801 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4803 SPStyle *query =
4804 sp_style_new (SP_ACTIVE_DOCUMENT);
4805 int result_numbers =
4806 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4808 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4809 if (result_numbers == QUERY_STYLE_NOTHING)
4810 {
4811 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4812 }
4814 sp_style_unref(query);
4816 sp_desktop_set_style (desktop, css, true, true);
4817 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4818 _("Text: Change font size"));
4819 sp_repr_css_attr_unref (css);
4821 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4822 }
4824 gboolean
4825 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
4826 {
4827 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4828 if (!desktop) return FALSE;
4830 if (!g_object_get_data (tbl, "esc-pressed")) {
4831 g_object_set_data (tbl, "enter-pressed", gpointer(1));
4832 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4833 sp_text_toolbox_size_changed (cbox, tbl);
4834 g_object_set_data (tbl, "enter-pressed", gpointer(0));
4835 }
4836 return FALSE; // I consumed the event
4837 }
4840 gboolean
4841 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4842 {
4843 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4844 if (!desktop) return FALSE;
4846 switch (get_group0_keyval (event)) {
4847 case GDK_Escape: // defocus
4848 g_object_set_data (tbl, "esc-pressed", gpointer(1));
4849 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4850 g_object_set_data (tbl, "esc-pressed", gpointer(0));
4851 return TRUE; // I consumed the event
4852 break;
4853 case GDK_Return: // defocus
4854 case GDK_KP_Enter:
4855 g_object_set_data (tbl, "enter-pressed", gpointer(1));
4856 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4857 sp_text_toolbox_size_changed (cbox, tbl);
4858 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4859 g_object_set_data (tbl, "enter-pressed", gpointer(0));
4860 return TRUE; // I consumed the event
4861 break;
4862 }
4863 return FALSE;
4864 }
4866 void
4867 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
4868 GObject *tbl)
4869 {
4870 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4871 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4872 int x, y;
4874 if (!popdown_visible)
4875 {
4876 gdk_window_get_origin (widget->window, &x, &y);
4877 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4878 gtk_widget_show_all (popdown);
4879 //sp_transientize (popdown);
4881 gdk_pointer_grab (widget->window, TRUE,
4882 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4883 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4884 GDK_POINTER_MOTION_MASK),
4885 NULL, NULL, GDK_CURRENT_TIME);
4887 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4889 popdown_visible = true;
4890 }
4891 else
4892 {
4893 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4894 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4895 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4896 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4897 gtk_widget_hide (popdown);
4898 popdown_visible = false;
4899 }
4900 }
4902 gboolean
4903 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
4904 GdkEventFocus */*event*/,
4905 GObject */*tbl*/)
4906 {
4907 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4908 return FALSE;
4909 }
4911 gboolean
4912 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
4913 GdkEventFocus */*event*/,
4914 GObject */*tbl*/)
4915 {
4916 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4918 if (popdown_hasfocus) {
4919 gtk_widget_hide (popdown);
4920 popdown_hasfocus = false;
4921 popdown_visible = false;
4922 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4923 return TRUE;
4924 }
4925 return FALSE;
4926 }
4928 gboolean
4929 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
4930 GdkEventFocus */*event*/,
4931 GObject */*tbl*/)
4932 {
4933 popdown_hasfocus = true;
4934 return TRUE;
4935 }
4938 void
4939 cell_data_func (GtkTreeViewColumn */*column*/,
4940 GtkCellRenderer *cell,
4941 GtkTreeModel *tree_model,
4942 GtkTreeIter *iter,
4943 gpointer /*data*/)
4944 {
4945 char *family,
4946 *family_escaped,
4947 *sample_escaped;
4949 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4951 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4953 family_escaped = g_markup_escape_text (family, -1);
4954 sample_escaped = g_markup_escape_text (sample, -1);
4956 std::stringstream markup;
4957 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4958 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4960 free (family);
4961 free (family_escaped);
4962 free (sample_escaped);
4963 }
4965 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4966 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4967 if (completion) {
4968 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4969 g_object_unref (completion);
4970 }
4971 }
4973 GtkWidget*
4974 sp_text_toolbox_new (SPDesktop *desktop)
4975 {
4976 GtkWidget *tbl = gtk_hbox_new (FALSE, 0);
4978 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4979 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4981 GtkTooltips *tt = gtk_tooltips_new();
4982 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4984 ////////////Family
4985 //Window
4986 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4987 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4989 //Entry
4990 GtkWidget *entry = gtk_entry_new ();
4991 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4992 GtkEntryCompletion *completion = gtk_entry_completion_new ();
4993 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4994 gtk_entry_completion_set_text_column (completion, 0);
4995 gtk_entry_completion_set_minimum_key_length (completion, 1);
4996 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4997 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4998 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4999 aux_toolbox_space (tbl, 1);
5000 gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
5001 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5003 //Button
5004 GtkWidget *button = gtk_button_new ();
5005 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5006 gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, 0);
5008 //Popdown
5009 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
5010 GtkWidget *treeview = gtk_tree_view_new ();
5012 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5013 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
5014 gtk_tree_view_column_pack_start (column, cell, FALSE);
5015 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5016 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5017 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5019 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5020 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5021 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5023 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5025 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5026 gtk_container_add (GTK_CONTAINER (sw), treeview);
5028 gtk_container_add (GTK_CONTAINER (window), sw);
5029 gtk_widget_set_size_request (window, 300, 450);
5031 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5032 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5033 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5035 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5037 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5038 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5039 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5041 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5042 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5044 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5045 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5046 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5047 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5048 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5050 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
5051 aux_toolbox_space (tbl, 1);
5052 GtkWidget *box = gtk_event_box_new ();
5053 gtk_container_add (GTK_CONTAINER (box), image);
5054 gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
5055 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5056 GtkTooltips *tooltips = gtk_tooltips_new ();
5057 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5058 gtk_widget_hide (GTK_WIDGET (box));
5059 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5061 ////////////Size
5062 const char *sizes[] = {
5063 "4", "6", "8", "9", "10", "11", "12", "13", "14",
5064 "16", "18", "20", "22", "24", "28",
5065 "32", "36", "40", "48", "56", "64", "72", "144"
5066 };
5068 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5069 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5070 gtk_widget_set_size_request (cbox, 80, -1);
5071 aux_toolbox_space (tbl, 1);
5072 gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
5073 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5074 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5075 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5076 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5078 //spacer
5079 aux_toolbox_space (tbl, 4);
5080 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5082 ////////////Text anchor
5083 GtkWidget *group = gtk_radio_button_new (NULL);
5084 GtkWidget *row = gtk_hbox_new (FALSE, 4);
5085 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5087 // left
5088 GtkWidget *rbutton = group;
5089 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5090 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
5091 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5093 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5094 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
5095 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5096 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5098 // center
5099 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5100 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5101 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
5102 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5104 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5105 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
5106 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5107 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5109 // right
5110 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5111 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5112 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
5113 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5115 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5116 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5117 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5118 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5120 // fill
5121 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5122 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5123 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
5124 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5126 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5127 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
5128 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5129 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5131 aux_toolbox_space (tbl, 1);
5132 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5134 //spacer
5135 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5137 ////////////Text style
5138 row = gtk_hbox_new (FALSE, 4);
5140 // bold
5141 rbutton = gtk_toggle_button_new ();
5142 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5143 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
5144 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5145 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5147 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5148 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
5149 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5151 // italic
5152 rbutton = gtk_toggle_button_new ();
5153 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5154 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
5155 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5156 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5158 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5159 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
5160 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5162 aux_toolbox_space (tbl, 1);
5163 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5165 //spacer
5166 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5168 ////////////Text orientation
5169 group = gtk_radio_button_new (NULL);
5170 row = gtk_hbox_new (FALSE, 4);
5171 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5173 // horizontal
5174 rbutton = group;
5175 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5176 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
5177 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5178 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5180 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5181 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5182 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5184 // vertical
5185 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5186 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5187 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
5188 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5189 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5191 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5192 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
5193 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5194 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5197 //watch selection
5198 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5200 sigc::connection *c_selection_changed =
5201 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5202 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5203 pool->add_connection ("selection-changed", c_selection_changed);
5205 sigc::connection *c_selection_modified =
5206 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5207 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5208 pool->add_connection ("selection-modified", c_selection_modified);
5210 sigc::connection *c_subselection_changed =
5211 new sigc::connection (desktop->connectToolSubselectionChanged
5212 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5213 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5215 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5218 gtk_widget_show_all (tbl);
5219 return tbl;
5221 } // end of sp_text_toolbox_new()
5223 }//<unnamed> namespace
5226 //#########################
5227 //## Connector ##
5228 //#########################
5230 static void sp_connector_path_set_avoid(void)
5231 {
5232 cc_selection_set_avoid(true);
5233 }
5236 static void sp_connector_path_set_ignore(void)
5237 {
5238 cc_selection_set_avoid(false);
5239 }
5243 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5244 {
5245 // quit if run by the _changed callbacks
5246 if (g_object_get_data( tbl, "freeze" )) {
5247 return;
5248 }
5250 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5251 SPDocument *doc = sp_desktop_document(desktop);
5253 if (!sp_document_get_undo_sensitive(doc))
5254 {
5255 return;
5256 }
5258 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5260 if ( repr->attribute("inkscape:connector-spacing") ) {
5261 gdouble priorValue = gtk_adjustment_get_value(adj);
5262 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5263 if ( priorValue == gtk_adjustment_get_value(adj) ) {
5264 return;
5265 }
5266 } else if ( adj->value == defaultConnSpacing ) {
5267 return;
5268 }
5270 // in turn, prevent callbacks from responding
5271 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5273 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5274 SP_OBJECT(desktop->namedview)->updateRepr();
5276 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5277 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5278 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5279 NR::Matrix m = NR::identity();
5280 avoid_item_move(&m, item);
5281 }
5283 if (items) {
5284 g_slist_free(items);
5285 }
5287 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5288 _("Change connector spacing"));
5290 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5292 spinbutton_defocus(GTK_OBJECT(tbl));
5293 }
5295 static void sp_connector_graph_layout(void)
5296 {
5297 if (!SP_ACTIVE_DESKTOP) return;
5299 // hack for clones, see comment in align-and-distribute.cpp
5300 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5301 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5303 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5305 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5307 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5308 }
5310 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5311 {
5312 if ( gtk_toggle_action_get_active( act ) ) {
5313 prefs_set_string_attribute("tools.connector", "directedlayout",
5314 "true");
5315 } else {
5316 prefs_set_string_attribute("tools.connector", "directedlayout",
5317 "false");
5318 }
5319 }
5321 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5322 {
5323 if ( gtk_toggle_action_get_active( act ) ) {
5324 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5325 "true");
5326 } else {
5327 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5328 "false");
5329 }
5330 }
5333 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5334 {
5335 prefs_set_double_attribute("tools.connector", "length", adj->value);
5336 spinbutton_defocus(GTK_OBJECT(tbl));
5337 }
5339 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5340 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5341 bool /*is_interactive*/, gpointer data)
5342 {
5343 GtkWidget *tbl = GTK_WIDGET(data);
5345 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5346 return;
5347 }
5348 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5349 return;
5350 }
5352 GtkAdjustment *adj = (GtkAdjustment*)
5353 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5354 gdouble spacing = defaultConnSpacing;
5355 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5357 gtk_adjustment_set_value(adj, spacing);
5358 }
5361 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5362 NULL, /* child_added */
5363 NULL, /* child_removed */
5364 connector_tb_event_attr_changed,
5365 NULL, /* content_changed */
5366 NULL /* order_changed */
5367 };
5370 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5371 {
5372 {
5373 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5374 _("Avoid"),
5375 _("Make connectors avoid selected objects"),
5376 "connector_avoid",
5377 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5378 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5379 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5380 }
5382 {
5383 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5384 _("Ignore"),
5385 _("Make connectors ignore selected objects"),
5386 "connector_ignore",
5387 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5388 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5389 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5390 }
5392 EgeAdjustmentAction* eact = 0;
5394 // Spacing spinbox
5395 eact = create_adjustment_action( "ConnectorSpacingAction",
5396 _("Connector Spacing"), _("Spacing:"),
5397 _("The amount of space left around objects by auto-routing connectors"),
5398 "tools.connector", "spacing", defaultConnSpacing,
5399 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5400 0, 100, 1.0, 10.0,
5401 0, 0, 0,
5402 connector_spacing_changed, 1, 0 );
5403 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5405 // Graph (connector network) layout
5406 {
5407 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5408 _("Graph"),
5409 _("Nicely arrange selected connector network"),
5410 "graph_layout",
5411 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5412 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5413 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5414 }
5416 // Default connector length spinbox
5417 eact = create_adjustment_action( "ConnectorLengthAction",
5418 _("Connector Length"), _("Length:"),
5419 _("Ideal length for connectors when layout is applied"),
5420 "tools.connector", "length", 100,
5421 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5422 10, 1000, 10.0, 100.0,
5423 0, 0, 0,
5424 connector_length_changed, 1, 0 );
5425 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5428 // Directed edges toggle button
5429 {
5430 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5431 _("Downwards"),
5432 _("Make connectors with end-markers (arrows) point downwards"),
5433 "directed_graph",
5434 Inkscape::ICON_SIZE_DECORATION );
5435 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5437 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5438 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5439 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5441 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5442 }
5444 // Avoid overlaps toggle button
5445 {
5446 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5447 _("Remove overlaps"),
5448 _("Do not allow overlapping shapes"),
5449 "remove_overlaps",
5450 Inkscape::ICON_SIZE_DECORATION );
5451 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5453 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5454 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5455 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5457 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5458 }
5460 // Code to watch for changes to the connector-spacing attribute in
5461 // the XML.
5462 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5463 g_assert(repr != NULL);
5465 purge_repr_listener( holder, holder );
5467 if (repr) {
5468 g_object_set_data( holder, "repr", repr );
5469 Inkscape::GC::anchor(repr);
5470 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5471 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5472 }
5473 } // end of sp_connector_toolbox_prep()
5476 //#########################
5477 //## Paintbucket ##
5478 //#########################
5480 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5481 {
5482 gint channels = ege_select_one_action_get_active( act );
5483 flood_channels_set_channels( channels );
5484 }
5486 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5487 {
5488 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5489 }
5491 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5492 {
5493 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5494 }
5496 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5497 {
5498 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5499 SPUnit const *unit = tracker->getActiveUnit();
5501 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5503 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5504 }
5506 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5507 {
5508 // FIXME: make defaults settable via Inkscape Options
5509 struct KeyValue {
5510 char const *key;
5511 double value;
5512 } const key_values[] = {
5513 {"threshold", 15},
5514 {"offset", 0.0}
5515 };
5517 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5518 KeyValue const &kv = key_values[i];
5519 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5520 if ( adj ) {
5521 gtk_adjustment_set_value(adj, kv.value);
5522 }
5523 }
5525 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5526 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5527 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5528 ege_select_one_action_set_active( autogap_action, 0 );
5529 }
5531 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5532 {
5533 EgeAdjustmentAction* eact = 0;
5535 {
5536 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5538 GList* items = 0;
5539 gint count = 0;
5540 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5541 {
5542 GtkTreeIter iter;
5543 gtk_list_store_append( model, &iter );
5544 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5545 count++;
5546 }
5547 g_list_free( items );
5548 items = 0;
5549 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5550 g_object_set( act1, "short_label", _("Fill by:"), NULL );
5551 ege_select_one_action_set_appearance( act1, "compact" );
5552 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5553 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5554 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5555 g_object_set_data( holder, "channels_action", act1 );
5556 }
5558 // Spacing spinbox
5559 {
5560 eact = create_adjustment_action(
5561 "ThresholdAction",
5562 _("Fill Threshold"), _("Threshold:"),
5563 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5564 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5565 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5566 0, 0, 0,
5567 paintbucket_threshold_changed, 1, 0 );
5569 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5570 }
5572 // Create the units menu.
5573 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5574 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5575 if (stored_unit)
5576 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5577 g_object_set_data( holder, "tracker", tracker );
5578 {
5579 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5580 gtk_action_group_add_action( mainActions, act );
5581 }
5583 // Offset spinbox
5584 {
5585 eact = create_adjustment_action(
5586 "OffsetAction",
5587 _("Grow/shrink by"), _("Grow/shrink by:"),
5588 _("The amount to grow (positive) or shrink (negative) the created fill path"),
5589 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5590 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5591 0, 0, 0,
5592 paintbucket_offset_changed, 1, 2);
5593 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5595 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5596 }
5598 /* Auto Gap */
5599 {
5600 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5602 GList* items = 0;
5603 gint count = 0;
5604 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5605 {
5606 GtkTreeIter iter;
5607 gtk_list_store_append( model, &iter );
5608 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5609 count++;
5610 }
5611 g_list_free( items );
5612 items = 0;
5613 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5614 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5615 ege_select_one_action_set_appearance( act2, "compact" );
5616 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5617 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5618 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5619 g_object_set_data( holder, "autogap_action", act2 );
5620 }
5622 /* Reset */
5623 {
5624 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5625 _("Defaults"),
5626 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5627 GTK_STOCK_CLEAR );
5628 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5629 gtk_action_group_add_action( mainActions, act );
5630 gtk_action_set_sensitive( act, TRUE );
5631 }
5633 }
5635 /*
5636 Local Variables:
5637 mode:c++
5638 c-file-style:"stroustrup"
5639 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5640 indent-tabs-mode:nil
5641 fill-column:99
5642 End:
5643 */
5644 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :