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 using Inkscape::UnitTracker;
108 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
109 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
111 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
124 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
130 static struct {
131 gchar const *type_name;
132 gchar const *data_name;
133 sp_verb_t verb;
134 sp_verb_t doubleclick_verb;
135 } const tools[] = {
136 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
137 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
138 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
139 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
140 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
141 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
142 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
143 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
144 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
145 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
146 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
147 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
148 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
149 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
150 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
151 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
152 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
153 { NULL, NULL, 0, 0 }
154 };
156 static struct {
157 gchar const *type_name;
158 gchar const *data_name;
159 GtkWidget *(*create_func)(SPDesktop *desktop);
160 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
161 gchar const *ui_name;
162 gint swatch_verb_id;
163 gchar const *swatch_tool;
164 gchar const *swatch_tip;
165 } const aux_toolboxes[] = {
166 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
167 SP_VERB_INVALID, 0, 0},
168 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
169 SP_VERB_INVALID, 0, 0},
170 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
171 SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
172 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
173 SP_VERB_INVALID, 0, 0},
174 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
175 SP_VERB_CONTEXT_STAR_PREFS, "tools.shapes.star", _("Style of new stars")},
176 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
177 SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", _("Style of new rectangles")},
178 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
179 SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", _("Style of new 3D boxes")},
180 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
181 SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", _("Style of new ellipses")},
182 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
183 SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral", _("Style of new spirals")},
184 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
185 SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
186 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
187 SP_VERB_CONTEXT_PEN_PREFS, "tools.freehand.pen", _("Style of new paths created by Pen")},
188 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
189 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
190 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
191 SP_VERB_INVALID, 0, 0},
192 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
193 SP_VERB_INVALID, 0, 0},
194 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
195 SP_VERB_INVALID, 0, 0},
196 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
197 SP_VERB_INVALID, 0, 0},
198 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
199 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
200 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
201 };
204 static gchar const * ui_descr =
205 "<ui>"
206 " <toolbar name='SelectToolbar'>"
207 " <toolitem action='EditSelectAll' />"
208 " <toolitem action='EditSelectAllInAllLayers' />"
209 " <toolitem action='EditDeselect' />"
210 " <separator />"
211 " <toolitem action='ObjectRotate90CCW' />"
212 " <toolitem action='ObjectRotate90' />"
213 " <toolitem action='ObjectFlipHorizontally' />"
214 " <toolitem action='ObjectFlipVertically' />"
215 " <separator />"
216 " <toolitem action='SelectionToBack' />"
217 " <toolitem action='SelectionLower' />"
218 " <toolitem action='SelectionRaise' />"
219 " <toolitem action='SelectionToFront' />"
220 " <separator />"
221 " <toolitem action='XAction' />"
222 " <toolitem action='YAction' />"
223 " <toolitem action='WidthAction' />"
224 " <toolitem action='LockAction' />"
225 " <toolitem action='HeightAction' />"
226 " <toolitem action='UnitsAction' />"
227 " <separator />"
228 " <toolitem action='transform_affect_label' />"
229 " <toolitem action='transform_stroke' />"
230 " <toolitem action='transform_corners' />"
231 " <toolitem action='transform_gradient' />"
232 " <toolitem action='transform_pattern' />"
233 " </toolbar>"
235 " <toolbar name='NodeToolbar'>"
236 " <toolitem action='NodeInsertAction' />"
237 " <toolitem action='NodeDeleteAction' />"
238 " <separator />"
239 " <toolitem action='NodeJoinAction' />"
240 " <toolitem action='NodeJoinSegmentAction' />"
241 " <toolitem action='NodeDeleteSegmentAction' />"
242 " <toolitem action='NodeBreakAction' />"
243 " <separator />"
244 " <toolitem action='NodeCuspAction' />"
245 " <toolitem action='NodeSmoothAction' />"
246 " <toolitem action='NodeSymmetricAction' />"
247 " <separator />"
248 " <toolitem action='NodeLineAction' />"
249 " <toolitem action='NodeCurveAction' />"
250 " <separator />"
251 " <toolitem action='ObjectToPath' />"
252 " <toolitem action='StrokeToPath' />"
253 " <separator />"
254 " <toolitem action='NodeXAction' />"
255 " <toolitem action='NodeYAction' />"
256 " <toolitem action='NodeUnitsAction' />"
257 " <separator />"
258 " <toolitem action='ObjectEditClipPathAction' />"
259 " <toolitem action='ObjectEditMaskPathAction' />"
260 " <toolitem action='EditNextLPEParameterAction' />"
261 " <separator />"
262 " <toolitem action='NodesShowHandlesAction' />"
263 " <toolitem action='NodesShowHelperpath' />"
264 " </toolbar>"
266 " <toolbar name='TweakToolbar'>"
267 " <toolitem action='TweakWidthAction' />"
268 " <separator />"
269 " <toolitem action='TweakForceAction' />"
270 " <toolitem action='TweakPressureAction' />"
271 " <separator />"
272 " <toolitem action='TweakModeAction' />"
273 " <separator />"
274 " <toolitem action='TweakFidelityAction' />"
275 " <separator />"
276 " <toolitem action='TweakChannelsLabel' />"
277 " <toolitem action='TweakDoH' />"
278 " <toolitem action='TweakDoS' />"
279 " <toolitem action='TweakDoL' />"
280 " <toolitem action='TweakDoO' />"
281 " </toolbar>"
283 " <toolbar name='ZoomToolbar'>"
284 " <toolitem action='ZoomIn' />"
285 " <toolitem action='ZoomOut' />"
286 " <separator />"
287 " <toolitem action='Zoom1:0' />"
288 " <toolitem action='Zoom1:2' />"
289 " <toolitem action='Zoom2:1' />"
290 " <separator />"
291 " <toolitem action='ZoomSelection' />"
292 " <toolitem action='ZoomDrawing' />"
293 " <toolitem action='ZoomPage' />"
294 " <toolitem action='ZoomPageWidth' />"
295 " <separator />"
296 " <toolitem action='ZoomPrev' />"
297 " <toolitem action='ZoomNext' />"
298 " </toolbar>"
300 " <toolbar name='StarToolbar'>"
301 " <separator />"
302 " <toolitem action='StarStateAction' />"
303 " <separator />"
304 " <toolitem action='FlatAction' />"
305 " <separator />"
306 " <toolitem action='MagnitudeAction' />"
307 " <toolitem action='SpokeAction' />"
308 " <toolitem action='RoundednessAction' />"
309 " <toolitem action='RandomizationAction' />"
310 " <separator />"
311 " <toolitem action='StarResetAction' />"
312 " </toolbar>"
314 " <toolbar name='RectToolbar'>"
315 " <toolitem action='RectStateAction' />"
316 " <toolitem action='RectWidthAction' />"
317 " <toolitem action='RectHeightAction' />"
318 " <toolitem action='RadiusXAction' />"
319 " <toolitem action='RadiusYAction' />"
320 " <toolitem action='RectUnitsAction' />"
321 " <separator />"
322 " <toolitem action='RectResetAction' />"
323 " </toolbar>"
325 " <toolbar name='3DBoxToolbar'>"
326 " <toolitem action='3DBoxAngleXAction' />"
327 " <toolitem action='3DBoxVPXStateAction' />"
328 " <separator />"
329 " <toolitem action='3DBoxAngleYAction' />"
330 " <toolitem action='3DBoxVPYStateAction' />"
331 " <separator />"
332 " <toolitem action='3DBoxAngleZAction' />"
333 " <toolitem action='3DBoxVPZStateAction' />"
334 " </toolbar>"
336 " <toolbar name='SpiralToolbar'>"
337 " <toolitem action='SpiralStateAction' />"
338 " <toolitem action='SpiralRevolutionAction' />"
339 " <toolitem action='SpiralExpansionAction' />"
340 " <toolitem action='SpiralT0Action' />"
341 " <separator />"
342 " <toolitem action='SpiralResetAction' />"
343 " </toolbar>"
345 " <toolbar name='PenToolbar'>"
346 " </toolbar>"
348 " <toolbar name='PencilToolbar'>"
349 " </toolbar>"
351 " <toolbar name='CalligraphyToolbar'>"
352 " <separator />"
353 " <toolitem action='SetProfileAction'/>"
354 " <toolitem action='SaveDeleteProfileAction'/>"
355 " <separator />"
356 " <toolitem action='CalligraphyWidthAction' />"
357 " <toolitem action='PressureAction' />"
358 " <toolitem action='TraceAction' />"
359 " <toolitem action='ThinningAction' />"
360 " <separator />"
361 " <toolitem action='AngleAction' />"
362 " <toolitem action='TiltAction' />"
363 " <toolitem action='FixationAction' />"
364 " <separator />"
365 " <toolitem action='CapRoundingAction' />"
366 " <separator />"
367 " <toolitem action='TremorAction' />"
368 " <toolitem action='WiggleAction' />"
369 " <toolitem action='MassAction' />"
370 " <separator />"
371 " </toolbar>"
373 " <toolbar name='ArcToolbar'>"
374 " <toolitem action='ArcStateAction' />"
375 " <separator />"
376 " <toolitem action='ArcStartAction' />"
377 " <toolitem action='ArcEndAction' />"
378 " <separator />"
379 " <toolitem action='ArcOpenAction' />"
380 " <separator />"
381 " <toolitem action='ArcResetAction' />"
382 " <separator />"
383 " </toolbar>"
385 " <toolbar name='PaintbucketToolbar'>"
386 " <toolitem action='ChannelsAction' />"
387 " <separator />"
388 " <toolitem action='ThresholdAction' />"
389 " <separator />"
390 " <toolitem action='OffsetAction' />"
391 " <toolitem action='PaintbucketUnitsAction' />"
392 " <separator />"
393 " <toolitem action='AutoGapAction' />"
394 " <separator />"
395 " <toolitem action='PaintbucketResetAction' />"
396 " </toolbar>"
398 " <toolbar name='DropperToolbar'>"
399 " <toolitem action='DropperOpacityAction' />"
400 " <toolitem action='DropperPickAlphaAction' />"
401 " <toolitem action='DropperSetAlphaAction' />"
402 " </toolbar>"
404 " <toolbar name='ConnectorToolbar'>"
405 " <toolitem action='ConnectorAvoidAction' />"
406 " <toolitem action='ConnectorIgnoreAction' />"
407 " <toolitem action='ConnectorSpacingAction' />"
408 " <toolitem action='ConnectorGraphAction' />"
409 " <toolitem action='ConnectorLengthAction' />"
410 " <toolitem action='ConnectorDirectedAction' />"
411 " <toolitem action='ConnectorOverlapAction' />"
412 " </toolbar>"
414 "</ui>"
415 ;
417 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
419 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
421 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
422 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
424 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
425 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
427 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
428 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
430 /* Global text entry widgets necessary for update */
431 /* GtkWidget *dropper_rgb_entry,
432 *dropper_opacity_entry ; */
433 // should be made a private member once this is converted to class
435 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
436 connection->disconnect();
437 delete connection;
438 }
440 static void purge_repr_listener( GObject* obj, GObject* tbl )
441 {
442 (void)obj;
443 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
444 if (oldrepr) { // remove old listener
445 sp_repr_remove_listener_by_data(oldrepr, tbl);
446 Inkscape::GC::release(oldrepr);
447 oldrepr = 0;
448 g_object_set_data( tbl, "repr", NULL );
449 }
450 }
452 GtkWidget *
453 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
454 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
455 Inkscape::UI::View::View *view, GtkTooltips *tt)
456 {
457 SPAction *action = verb->get_action(view);
458 if (!action) return NULL;
460 SPAction *doubleclick_action;
461 if (doubleclick_verb)
462 doubleclick_action = doubleclick_verb->get_action(view);
463 else
464 doubleclick_action = NULL;
466 /* fixme: Handle sensitive/unsensitive */
467 /* fixme: Implement sp_button_new_from_action */
468 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
469 gtk_widget_show(b);
470 gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
472 return b;
473 }
475 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
476 Inkscape::UI::View::View *view, GtkTooltips *tt)
477 {
478 return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
479 }
481 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
482 Inkscape::UI::View::View *view, GtkTooltips *tt)
483 {
484 return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
485 }
488 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
489 {
490 SPAction* targetAction = SP_ACTION(user_data);
491 if ( targetAction ) {
492 sp_action_perform( targetAction, NULL );
493 }
494 }
496 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
497 {
498 if ( data ) {
499 GtkAction* act = GTK_ACTION(data);
500 gtk_action_set_sensitive( act, sensitive );
501 }
502 }
504 static SPActionEventVector action_event_vector = {
505 {NULL},
506 NULL,
507 NULL,
508 sp_action_action_set_sensitive,
509 NULL,
510 NULL
511 };
513 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
514 {
515 GtkAction* act = 0;
517 SPAction* targetAction = verb->get_action(view);
518 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
519 act = GTK_ACTION(inky);
520 gtk_action_set_sensitive( act, targetAction->sensitive );
522 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
524 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
525 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
527 return act;
528 }
530 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
531 {
532 Inkscape::UI::View::View *view = desktop;
533 gint verbsToUse[] = {
534 // disabled until we have icons for them:
535 //find
536 //SP_VERB_EDIT_TILE,
537 //SP_VERB_EDIT_UNTILE,
538 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
539 SP_VERB_DIALOG_DISPLAY,
540 SP_VERB_DIALOG_FILL_STROKE,
541 SP_VERB_DIALOG_NAMEDVIEW,
542 SP_VERB_DIALOG_TEXT,
543 SP_VERB_DIALOG_XML_EDITOR,
544 SP_VERB_EDIT_CLONE,
545 SP_VERB_EDIT_COPY,
546 SP_VERB_EDIT_CUT,
547 SP_VERB_EDIT_DUPLICATE,
548 SP_VERB_EDIT_PASTE,
549 SP_VERB_EDIT_REDO,
550 SP_VERB_EDIT_UNDO,
551 SP_VERB_EDIT_UNLINK_CLONE,
552 SP_VERB_FILE_EXPORT,
553 SP_VERB_FILE_IMPORT,
554 SP_VERB_FILE_NEW,
555 SP_VERB_FILE_OPEN,
556 SP_VERB_FILE_PRINT,
557 SP_VERB_FILE_SAVE,
558 SP_VERB_OBJECT_TO_CURVE,
559 SP_VERB_SELECTION_GROUP,
560 SP_VERB_SELECTION_OUTLINE,
561 SP_VERB_SELECTION_UNGROUP,
562 SP_VERB_ZOOM_1_1,
563 SP_VERB_ZOOM_1_2,
564 SP_VERB_ZOOM_2_1,
565 SP_VERB_ZOOM_DRAWING,
566 SP_VERB_ZOOM_IN,
567 SP_VERB_ZOOM_NEXT,
568 SP_VERB_ZOOM_OUT,
569 SP_VERB_ZOOM_PAGE,
570 SP_VERB_ZOOM_PAGE_WIDTH,
571 SP_VERB_ZOOM_PREV,
572 SP_VERB_ZOOM_SELECTION,
573 };
575 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
576 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
578 static std::map<SPDesktop*, GtkActionGroup*> groups;
579 GtkActionGroup* mainActions = 0;
580 if ( groups.find(desktop) != groups.end() ) {
581 mainActions = groups[desktop];
582 }
584 if ( !mainActions ) {
585 mainActions = gtk_action_group_new("main");
586 groups[desktop] = mainActions;
587 }
589 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
590 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
591 if ( verb ) {
592 if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
593 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
594 gtk_action_group_add_action( mainActions, act );
595 }
596 }
597 }
599 return mainActions;
600 }
603 GtkWidget *
604 sp_tool_toolbox_new()
605 {
606 GtkTooltips *tt = gtk_tooltips_new();
607 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
609 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
610 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
612 gtk_widget_set_sensitive(tb, FALSE);
614 GtkWidget *hb = gtk_handle_box_new();
615 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
616 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
617 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
619 gtk_container_add(GTK_CONTAINER(hb), tb);
620 gtk_widget_show(GTK_WIDGET(tb));
622 sigc::connection* conn = new sigc::connection;
623 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
625 return hb;
626 }
628 static void
629 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
630 {
631 g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
632 gtk_widget_queue_resize(child);
633 }
635 static void
636 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
637 {
638 g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
639 gtk_widget_queue_resize(child);
640 }
642 GtkWidget *
643 sp_aux_toolbox_new()
644 {
645 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
647 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
649 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
651 gtk_widget_set_sensitive(tb, FALSE);
653 GtkWidget *hb = gtk_handle_box_new();
654 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
655 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
656 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
658 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
659 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
661 gtk_container_add(GTK_CONTAINER(hb), tb);
662 gtk_widget_show(GTK_WIDGET(tb));
664 sigc::connection* conn = new sigc::connection;
665 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
667 return hb;
668 }
670 //####################################
671 //# Commands Bar
672 //####################################
674 GtkWidget *
675 sp_commands_toolbox_new()
676 {
677 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
679 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
681 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
682 gtk_widget_set_sensitive(tb, FALSE);
684 GtkWidget *hb = gtk_handle_box_new();
685 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
686 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
687 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
689 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
690 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
692 gtk_container_add(GTK_CONTAINER(hb), tb);
693 gtk_widget_show(GTK_WIDGET(tb));
695 sigc::connection* conn = new sigc::connection;
696 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
698 return hb;
699 }
701 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
702 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
703 gchar const *path, gchar const *data, gdouble def,
704 GtkWidget *focusTarget,
705 GtkWidget *us,
706 GObject *dataKludge,
707 gboolean altx, gchar const *altx_mark,
708 gdouble lower, gdouble upper, gdouble step, gdouble page,
709 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
710 void (*callback)(GtkAdjustment *, GObject *),
711 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
712 {
713 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
714 lower, upper, step, page, page ) );
715 if (us) {
716 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
717 }
719 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
721 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
722 if ( shortLabel ) {
723 g_object_set( act, "short_label", shortLabel, NULL );
724 }
726 if ( (descrCount > 0) && descrLabels && descrValues ) {
727 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
728 }
730 if ( focusTarget ) {
731 ege_adjustment_action_set_focuswidget( act, focusTarget );
732 }
734 if ( altx && altx_mark ) {
735 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
736 }
738 if ( dataKludge ) {
739 g_object_set_data( dataKludge, data, adj );
740 }
742 // Using a cast just to make sure we pass in the right kind of function pointer
743 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
745 return act;
746 }
749 //####################################
750 //# node editing callbacks
751 //####################################
753 /**
754 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
755 */
756 static ShapeEditor *get_current_shape_editor()
757 {
758 if (!SP_ACTIVE_DESKTOP) {
759 return NULL;
760 }
762 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
764 if (!SP_IS_NODE_CONTEXT(event_context)) {
765 return NULL;
766 }
768 return SP_NODE_CONTEXT(event_context)->shape_editor;
769 }
772 void
773 sp_node_path_edit_add(void)
774 {
775 ShapeEditor *shape_editor = get_current_shape_editor();
776 if (shape_editor) shape_editor->add_node();
777 }
779 void
780 sp_node_path_edit_delete(void)
781 {
782 ShapeEditor *shape_editor = get_current_shape_editor();
783 if (shape_editor) shape_editor->delete_nodes();
784 }
786 void
787 sp_node_path_edit_delete_segment(void)
788 {
789 ShapeEditor *shape_editor = get_current_shape_editor();
790 if (shape_editor) shape_editor->delete_segment();
791 }
793 void
794 sp_node_path_edit_break(void)
795 {
796 ShapeEditor *shape_editor = get_current_shape_editor();
797 if (shape_editor) shape_editor->break_at_nodes();
798 }
800 void
801 sp_node_path_edit_join(void)
802 {
803 ShapeEditor *shape_editor = get_current_shape_editor();
804 if (shape_editor) shape_editor->join_nodes();
805 }
807 void
808 sp_node_path_edit_join_segment(void)
809 {
810 ShapeEditor *shape_editor = get_current_shape_editor();
811 if (shape_editor) shape_editor->join_segments();
812 }
814 void
815 sp_node_path_edit_toline(void)
816 {
817 ShapeEditor *shape_editor = get_current_shape_editor();
818 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
819 }
821 void
822 sp_node_path_edit_tocurve(void)
823 {
824 ShapeEditor *shape_editor = get_current_shape_editor();
825 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
826 }
828 void
829 sp_node_path_edit_cusp(void)
830 {
831 ShapeEditor *shape_editor = get_current_shape_editor();
832 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
833 }
835 void
836 sp_node_path_edit_smooth(void)
837 {
838 ShapeEditor *shape_editor = get_current_shape_editor();
839 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
840 }
842 void
843 sp_node_path_edit_symmetrical(void)
844 {
845 ShapeEditor *shape_editor = get_current_shape_editor();
846 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
847 }
849 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
850 bool show = gtk_toggle_action_get_active( act );
851 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
852 ShapeEditor *shape_editor = get_current_shape_editor();
853 if (shape_editor) shape_editor->show_handles(show);
854 }
856 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
857 bool show = gtk_toggle_action_get_active( act );
858 prefs_set_int_attribute ("tools.nodes", "show_helperpath", show ? 1 : 0);
859 ShapeEditor *shape_editor = get_current_shape_editor();
860 if (shape_editor) shape_editor->show_helperpath(show);
861 }
863 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
864 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
865 }
867 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
868 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
869 }
871 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
872 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
873 }
875 /* is called when the node selection is modified */
876 static void
877 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
878 {
879 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
880 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
881 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
882 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
884 // quit if run by the attr_changed listener
885 if (g_object_get_data( tbl, "freeze" )) {
886 return;
887 }
889 // in turn, prevent listener from responding
890 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
892 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
893 SPUnit const *unit = tracker->getActiveUnit();
895 ShapeEditor *shape_editor = get_current_shape_editor();
896 if (shape_editor && shape_editor->has_nodepath()) {
897 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
898 int n_selected = 0;
899 if (nodepath) {
900 n_selected = nodepath->numSelected();
901 }
903 if (n_selected == 0) {
904 gtk_action_set_sensitive(xact, FALSE);
905 gtk_action_set_sensitive(yact, FALSE);
906 } else {
907 gtk_action_set_sensitive(xact, TRUE);
908 gtk_action_set_sensitive(yact, TRUE);
909 NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
910 NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
912 if (n_selected == 1) {
913 NR::Point sel_node = nodepath->singleSelectedCoords();
914 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
915 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
916 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
917 }
918 } else {
919 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
920 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
921 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
922 /* Note: Currently x and y will always have a value, even if the coordinates of the
923 selected nodes don't coincide (in this case we use the coordinates of the center
924 of the bounding box). So the entries are never set to zero. */
925 // FIXME: Maybe we should clear the entry if several nodes are selected
926 // instead of providing a kind of average value
927 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
928 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
929 }
930 }
931 }
932 } else {
933 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
934 gtk_action_set_sensitive(xact, FALSE);
935 gtk_action_set_sensitive(yact, FALSE);
936 }
938 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
939 }
941 static void
942 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
943 {
944 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
946 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
947 SPUnit const *unit = tracker->getActiveUnit();
949 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
950 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
951 }
953 // quit if run by the attr_changed listener
954 if (g_object_get_data( tbl, "freeze" )) {
955 return;
956 }
958 // in turn, prevent listener from responding
959 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
961 ShapeEditor *shape_editor = get_current_shape_editor();
962 if (shape_editor && shape_editor->has_nodepath()) {
963 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
964 if (!strcmp(value_name, "x")) {
965 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
966 }
967 if (!strcmp(value_name, "y")) {
968 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
969 }
970 }
972 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
973 }
975 static void
976 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
977 {
978 sp_node_path_value_changed(adj, tbl, "x");
979 }
981 static void
982 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
983 {
984 sp_node_path_value_changed(adj, tbl, "y");
985 }
987 void
988 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
989 {
990 {
991 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
992 SPItem *item = selection->singleItem();
993 if (item && SP_IS_SHAPE(item)) {
994 LivePathEffectObject *lpeobj = sp_shape_get_livepatheffectobject(SP_SHAPE(item));
995 if (lpeobj) {
996 gtk_action_set_sensitive(w, TRUE);
997 } else {
998 gtk_action_set_sensitive(w, FALSE);
999 }
1000 } else {
1001 gtk_action_set_sensitive(w, FALSE);
1002 }
1003 }
1005 {
1006 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1007 SPItem *item = selection->singleItem();
1008 if (item && item->clip_ref && item->clip_ref->getObject()) {
1009 gtk_action_set_sensitive(w, TRUE);
1010 } else {
1011 gtk_action_set_sensitive(w, FALSE);
1012 }
1013 }
1015 {
1016 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1017 SPItem *item = selection->singleItem();
1018 if (item && item->mask_ref && item->mask_ref->getObject()) {
1019 gtk_action_set_sensitive(w, TRUE);
1020 } else {
1021 gtk_action_set_sensitive(w, FALSE);
1022 }
1023 }
1024 }
1026 void
1027 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1028 {
1029 sp_node_toolbox_sel_changed (selection, tbl);
1030 }
1034 //################################
1035 //## Node Editing Toolbox ##
1036 //################################
1038 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1039 {
1040 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1041 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1042 g_object_set_data( holder, "tracker", tracker );
1044 {
1045 InkAction* inky = ink_action_new( "NodeInsertAction",
1046 _("Insert node"),
1047 _("Insert new nodes into selected segments"),
1048 "node_insert",
1049 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1050 g_object_set( inky, "short_label", _("Insert"), NULL );
1051 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1052 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1053 }
1055 {
1056 InkAction* inky = ink_action_new( "NodeDeleteAction",
1057 _("Delete node"),
1058 _("Delete selected nodes"),
1059 "node_delete",
1060 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1061 g_object_set( inky, "short_label", _("Delete"), NULL );
1062 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1063 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1064 }
1066 {
1067 InkAction* inky = ink_action_new( "NodeJoinAction",
1068 _("Join endnodes"),
1069 _("Join selected endnodes"),
1070 "node_join",
1071 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1072 g_object_set( inky, "short_label", _("Join"), NULL );
1073 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1074 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1075 }
1077 {
1078 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1079 _("Join Segment"),
1080 _("Join selected endnodes with a new segment"),
1081 "node_join_segment",
1082 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1083 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1084 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1085 }
1087 {
1088 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1089 _("Delete Segment"),
1090 _("Split path between two non-endpoint nodes"),
1091 "node_delete_segment",
1092 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1093 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1094 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1095 }
1097 {
1098 InkAction* inky = ink_action_new( "NodeBreakAction",
1099 _("Node Break"),
1100 _("Break path at selected nodes"),
1101 "node_break",
1102 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1103 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1104 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1105 }
1107 {
1108 InkAction* inky = ink_action_new( "NodeCuspAction",
1109 _("Node Cusp"),
1110 _("Make selected nodes corner"),
1111 "node_cusp",
1112 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1113 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1114 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1115 }
1117 {
1118 InkAction* inky = ink_action_new( "NodeSmoothAction",
1119 _("Node Smooth"),
1120 _("Make selected nodes smooth"),
1121 "node_smooth",
1122 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1123 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1124 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1125 }
1127 {
1128 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1129 _("Node Symmetric"),
1130 _("Make selected nodes symmetric"),
1131 "node_symmetric",
1132 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1133 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1134 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1135 }
1137 {
1138 InkAction* inky = ink_action_new( "NodeLineAction",
1139 _("Node Line"),
1140 _("Make selected segments lines"),
1141 "node_line",
1142 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1143 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1144 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1145 }
1147 {
1148 InkAction* inky = ink_action_new( "NodeCurveAction",
1149 _("Node Curve"),
1150 _("Make selected segments curves"),
1151 "node_curve",
1152 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1153 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1154 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1155 }
1157 {
1158 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1159 _("Show Handles"),
1160 _("Show the Bezier handles of selected nodes"),
1161 "nodes_show_handles",
1162 Inkscape::ICON_SIZE_DECORATION );
1163 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1164 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1165 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1166 }
1168 {
1169 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1170 _("Show Outline"),
1171 _("Show the outline of the path"),
1172 "nodes_show_helperpath",
1173 Inkscape::ICON_SIZE_DECORATION );
1174 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1175 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1176 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1177 }
1179 {
1180 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1181 _("Next path effect parameter"),
1182 _("Show next path effect parameter for editing"),
1183 "edit_next_parameter",
1184 Inkscape::ICON_SIZE_DECORATION );
1185 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1186 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1187 g_object_set_data( holder, "nodes_lpeedit", inky);
1188 }
1190 {
1191 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1192 _("Edit clipping path"),
1193 _("Edit the clipping path of the object"),
1194 "nodeedit-clippath",
1195 Inkscape::ICON_SIZE_DECORATION );
1196 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1197 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1198 g_object_set_data( holder, "nodes_clippathedit", inky);
1199 }
1201 {
1202 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1203 _("Edit mask path"),
1204 _("Edit the mask of the object"),
1205 "nodeedit-mask",
1206 Inkscape::ICON_SIZE_DECORATION );
1207 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1208 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1209 g_object_set_data( holder, "nodes_maskedit", inky);
1210 }
1212 /* X coord of selected node(s) */
1213 {
1214 EgeAdjustmentAction* eact = 0;
1215 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1216 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1217 eact = create_adjustment_action( "NodeXAction",
1218 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1219 "tools.nodes", "Xcoord", 0,
1220 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1221 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1222 labels, values, G_N_ELEMENTS(labels),
1223 sp_node_path_x_value_changed );
1224 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1225 g_object_set_data( holder, "nodes_x_action", eact );
1226 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1227 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1228 }
1230 /* Y coord of selected node(s) */
1231 {
1232 EgeAdjustmentAction* eact = 0;
1233 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1234 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1235 eact = create_adjustment_action( "NodeYAction",
1236 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1237 "tools.nodes", "Ycoord", 0,
1238 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1239 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1240 labels, values, G_N_ELEMENTS(labels),
1241 sp_node_path_y_value_changed );
1242 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1243 g_object_set_data( holder, "nodes_y_action", eact );
1244 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1245 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1246 }
1248 // add the units menu
1249 {
1250 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1251 gtk_action_group_add_action( mainActions, act );
1252 }
1255 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1257 //watch selection
1258 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1260 sigc::connection *c_selection_changed =
1261 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1262 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1263 pool->add_connection ("selection-changed", c_selection_changed);
1265 sigc::connection *c_selection_modified =
1266 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1267 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1268 pool->add_connection ("selection-modified", c_selection_modified);
1270 sigc::connection *c_subselection_changed =
1271 new sigc::connection (desktop->connectToolSubselectionChanged
1272 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1273 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1275 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1277 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1278 } // end of sp_node_toolbox_prep()
1281 //########################
1282 //## Zoom Toolbox ##
1283 //########################
1285 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1286 {
1287 // no custom GtkAction setup needed
1288 } // end of sp_zoom_toolbox_prep()
1290 void
1291 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1292 {
1293 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")));
1294 }
1297 void
1298 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1299 {
1300 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")));
1301 }
1303 void
1304 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1305 {
1306 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")));
1307 }
1309 static void
1310 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1311 {
1312 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1313 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1315 if (old_desktop) {
1316 GList *children, *iter;
1318 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1319 for ( iter = children ; iter ; iter = iter->next ) {
1320 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1321 }
1322 g_list_free(children);
1323 }
1325 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1327 if (desktop) {
1328 gtk_widget_set_sensitive(toolbox, TRUE);
1329 setup_func(toolbox, desktop);
1330 update_func(desktop, desktop->event_context, toolbox);
1331 *conn = desktop->connectEventContextChanged
1332 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1333 } else {
1334 gtk_widget_set_sensitive(toolbox, FALSE);
1335 }
1337 } // end of toolbox_set_desktop()
1340 static void
1341 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1342 {
1343 GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1344 gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1345 if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1346 // "toolbox.tools" was not set. Fallback to older value
1347 shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1349 // Copy the setting forwards
1350 prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1351 }
1352 Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1354 for (int i = 0 ; tools[i].type_name ; i++ ) {
1355 GtkWidget *button =
1356 sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1357 SP_BUTTON_TYPE_TOGGLE,
1358 Inkscape::Verb::get(tools[i].verb),
1359 Inkscape::Verb::get(tools[i].doubleclick_verb),
1360 desktop,
1361 tooltips );
1363 g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1364 (gpointer)button );
1365 }
1366 }
1369 static void
1370 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1371 {
1372 gchar const *const tname = ( eventcontext
1373 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1374 : NULL );
1375 for (int i = 0 ; tools[i].type_name ; i++ ) {
1376 SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1377 sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1378 }
1379 }
1381 static void
1382 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1383 {
1384 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1385 GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1386 GtkUIManager* mgr = gtk_ui_manager_new();
1387 GError* errVal = 0;
1388 gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1389 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1391 std::map<std::string, GtkWidget*> dataHolders;
1393 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1394 if ( aux_toolboxes[i].prep_func ) {
1395 // converted to GtkActions and UIManager
1397 GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1398 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1399 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1400 dataHolders[aux_toolboxes[i].type_name] = kludge;
1401 aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1402 } else {
1404 GtkWidget *sub_toolbox = 0;
1405 if (aux_toolboxes[i].create_func == NULL)
1406 sub_toolbox = sp_empty_toolbox_new(desktop);
1407 else {
1408 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1409 }
1411 gtk_size_group_add_widget( grouper, sub_toolbox );
1413 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1414 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1416 }
1417 }
1419 // Second pass to create toolbars *after* all GtkActions are created
1420 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1421 if ( aux_toolboxes[i].prep_func ) {
1422 // converted to GtkActions and UIManager
1424 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1426 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1427 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1429 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1430 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1431 g_free( tmp );
1432 tmp = 0;
1434 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1435 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1436 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1437 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1438 }
1439 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1442 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1444 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1445 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1446 swatch->setDesktop( desktop );
1447 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1448 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1449 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1450 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 );
1451 }
1453 gtk_widget_show_all( holder );
1454 sp_set_font_size_smaller( holder );
1456 gtk_size_group_add_widget( grouper, holder );
1458 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1459 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1460 }
1461 }
1463 g_object_unref( G_OBJECT(grouper) );
1464 }
1466 static void
1467 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1468 {
1469 gchar const *tname = ( eventcontext
1470 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1471 : NULL );
1472 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1473 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1474 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1475 gtk_widget_show_all(sub_toolbox);
1476 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1477 } else {
1478 gtk_widget_hide(sub_toolbox);
1479 }
1480 }
1481 }
1483 static void
1484 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1485 {
1486 gchar const * descr =
1487 "<ui>"
1488 " <toolbar name='CommandsToolbar'>"
1489 " <toolitem action='FileNew' />"
1490 " <toolitem action='FileOpen' />"
1491 " <toolitem action='FileSave' />"
1492 " <toolitem action='FilePrint' />"
1493 " <separator />"
1494 " <toolitem action='FileImport' />"
1495 " <toolitem action='FileExport' />"
1496 " <separator />"
1497 " <toolitem action='EditUndo' />"
1498 " <toolitem action='EditRedo' />"
1499 " <separator />"
1500 " <toolitem action='EditCopy' />"
1501 " <toolitem action='EditCut' />"
1502 " <toolitem action='EditPaste' />"
1503 " <separator />"
1504 " <toolitem action='ZoomSelection' />"
1505 " <toolitem action='ZoomDrawing' />"
1506 " <toolitem action='ZoomPage' />"
1507 " <separator />"
1508 " <toolitem action='EditDuplicate' />"
1509 " <toolitem action='EditClone' />"
1510 " <toolitem action='EditUnlinkClone' />"
1511 " <separator />"
1512 " <toolitem action='SelectionGroup' />"
1513 " <toolitem action='SelectionUnGroup' />"
1514 " <separator />"
1515 " <toolitem action='DialogFillStroke' />"
1516 " <toolitem action='DialogText' />"
1517 " <toolitem action='DialogXMLEditor' />"
1518 " <toolitem action='DialogAlignDistribute' />"
1519 " <separator />"
1520 " <toolitem action='DialogPreferences' />"
1521 " <toolitem action='DialogDocumentProperties' />"
1522 " </toolbar>"
1523 "</ui>";
1524 GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1527 GtkUIManager* mgr = gtk_ui_manager_new();
1528 GError* errVal = 0;
1530 gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1531 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1533 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1534 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1535 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1536 }
1537 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1538 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1539 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1542 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1543 }
1545 static void
1546 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1547 {
1548 }
1550 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1551 {
1552 gtk_widget_show(toolbox_toplevel);
1553 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1555 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1556 if (!shown_toolbox) {
1557 return;
1558 }
1559 gtk_widget_show(toolbox);
1561 gtk_widget_show_all(shown_toolbox);
1562 }
1564 void
1565 aux_toolbox_space(GtkWidget *tb, gint space)
1566 {
1567 gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1568 }
1570 static GtkWidget *
1571 sp_empty_toolbox_new(SPDesktop *desktop)
1572 {
1573 GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1574 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1575 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1577 gtk_widget_show_all(tbl);
1578 sp_set_font_size_smaller (tbl);
1580 return tbl;
1581 }
1583 // helper UI functions
1585 GtkWidget *
1586 sp_tb_spinbutton(
1587 gchar *label, gchar const *tooltip,
1588 gchar const *path, gchar const *data, gdouble def,
1589 GtkWidget *us,
1590 GtkWidget *tbl,
1591 gboolean altx, gchar const *altx_mark,
1592 gdouble lower, gdouble upper, gdouble step, gdouble page,
1593 void (*callback)(GtkAdjustment *, GtkWidget *),
1594 gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1595 {
1596 GtkTooltips *tt = gtk_tooltips_new();
1598 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1600 GtkWidget *l = gtk_label_new(label);
1601 gtk_widget_show(l);
1602 gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1603 gtk_container_add(GTK_CONTAINER(hb), l);
1605 GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1606 lower, upper, step, page, page);
1607 gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1608 if (us)
1609 sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1611 GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1612 gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1613 if (altx)
1614 gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1615 gtk_widget_set_size_request(sb,
1616 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1617 AUX_SPINBUTTON_HEIGHT);
1618 gtk_widget_show(sb);
1619 gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1620 gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1621 gtk_container_add(GTK_CONTAINER(hb), sb);
1622 gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1624 return hb;
1625 }
1627 #define MODE_LABEL_WIDTH 70
1629 //########################
1630 //## Star ##
1631 //########################
1633 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1634 {
1635 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1637 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1638 // do not remember prefs if this call is initiated by an undo change, because undoing object
1639 // creation sets bogus values to its attributes before it is deleted
1640 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1641 }
1643 // quit if run by the attr_changed listener
1644 if (g_object_get_data( dataKludge, "freeze" )) {
1645 return;
1646 }
1648 // in turn, prevent listener from responding
1649 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1651 bool modmade = false;
1653 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1654 GSList const *items = selection->itemList();
1655 for (; items != NULL; items = items->next) {
1656 if (SP_IS_STAR((SPItem *) items->data)) {
1657 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1658 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1659 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1660 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1661 + M_PI / (gint)adj->value));
1662 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1663 modmade = true;
1664 }
1665 }
1666 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1667 _("Star: Change number of corners"));
1669 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1670 }
1672 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1673 {
1674 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1676 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1677 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1678 }
1680 // quit if run by the attr_changed listener
1681 if (g_object_get_data( dataKludge, "freeze" )) {
1682 return;
1683 }
1685 // in turn, prevent listener from responding
1686 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1688 bool modmade = false;
1689 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1690 GSList const *items = selection->itemList();
1691 for (; items != NULL; items = items->next) {
1692 if (SP_IS_STAR((SPItem *) items->data)) {
1693 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1695 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1696 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1697 if (r2 < r1) {
1698 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1699 } else {
1700 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1701 }
1703 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1704 modmade = true;
1705 }
1706 }
1708 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1709 _("Star: Change spoke ratio"));
1711 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1712 }
1714 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1715 {
1716 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1717 bool flat = ege_select_one_action_get_active( act ) == 0;
1719 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1720 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1721 flat ? "true" : "false" );
1722 }
1724 // quit if run by the attr_changed listener
1725 if (g_object_get_data( dataKludge, "freeze" )) {
1726 return;
1727 }
1729 // in turn, prevent listener from responding
1730 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1732 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1733 GSList const *items = selection->itemList();
1734 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1735 bool modmade = false;
1737 if ( prop_action ) {
1738 gtk_action_set_sensitive( prop_action, !flat );
1739 }
1741 for (; items != NULL; items = items->next) {
1742 if (SP_IS_STAR((SPItem *) items->data)) {
1743 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1744 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1745 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1746 modmade = true;
1747 }
1748 }
1750 if (modmade) {
1751 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1752 flat ? _("Make polygon") : _("Make star"));
1753 }
1755 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1756 }
1758 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1759 {
1760 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1762 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1763 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1764 }
1766 // quit if run by the attr_changed listener
1767 if (g_object_get_data( dataKludge, "freeze" )) {
1768 return;
1769 }
1771 // in turn, prevent listener from responding
1772 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1774 bool modmade = false;
1776 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1777 GSList const *items = selection->itemList();
1778 for (; items != NULL; items = items->next) {
1779 if (SP_IS_STAR((SPItem *) items->data)) {
1780 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1781 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1782 SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1783 modmade = true;
1784 }
1785 }
1786 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1787 _("Star: Change rounding"));
1789 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1790 }
1792 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1793 {
1794 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1796 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1797 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1798 }
1800 // quit if run by the attr_changed listener
1801 if (g_object_get_data( dataKludge, "freeze" )) {
1802 return;
1803 }
1805 // in turn, prevent listener from responding
1806 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1808 bool modmade = false;
1810 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1811 GSList const *items = selection->itemList();
1812 for (; items != NULL; items = items->next) {
1813 if (SP_IS_STAR((SPItem *) items->data)) {
1814 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1815 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1816 SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1817 modmade = true;
1818 }
1819 }
1820 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1821 _("Star: Change randomization"));
1823 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1824 }
1827 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1828 gchar const */*old_value*/, gchar const */*new_value*/,
1829 bool /*is_interactive*/, gpointer data)
1830 {
1831 GtkWidget *tbl = GTK_WIDGET(data);
1833 // quit if run by the _changed callbacks
1834 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1835 return;
1836 }
1838 // in turn, prevent callbacks from responding
1839 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1841 GtkAdjustment *adj = 0;
1843 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1844 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1846 if (!strcmp(name, "inkscape:randomized")) {
1847 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1848 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1849 } else if (!strcmp(name, "inkscape:rounded")) {
1850 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1851 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1852 } else if (!strcmp(name, "inkscape:flatsided")) {
1853 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1854 char const *flatsides = repr->attribute("inkscape:flatsided");
1855 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1856 if ( flatsides && !strcmp(flatsides,"false") ) {
1857 ege_select_one_action_set_active( flat_action, 1 );
1858 gtk_action_set_sensitive( prop_action, TRUE );
1859 } else {
1860 ege_select_one_action_set_active( flat_action, 0 );
1861 gtk_action_set_sensitive( prop_action, FALSE );
1862 }
1863 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1864 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1865 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1866 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1867 if (r2 < r1) {
1868 gtk_adjustment_set_value(adj, r2/r1);
1869 } else {
1870 gtk_adjustment_set_value(adj, r1/r2);
1871 }
1872 } else if (!strcmp(name, "sodipodi:sides")) {
1873 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1874 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1875 }
1877 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1878 }
1881 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1882 {
1883 NULL, /* child_added */
1884 NULL, /* child_removed */
1885 star_tb_event_attr_changed,
1886 NULL, /* content_changed */
1887 NULL /* order_changed */
1888 };
1891 /**
1892 * \param selection Should not be NULL.
1893 */
1894 static void
1895 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1896 {
1897 int n_selected = 0;
1898 Inkscape::XML::Node *repr = NULL;
1900 purge_repr_listener( tbl, tbl );
1902 for (GSList const *items = selection->itemList();
1903 items != NULL;
1904 items = items->next)
1905 {
1906 if (SP_IS_STAR((SPItem *) items->data)) {
1907 n_selected++;
1908 repr = SP_OBJECT_REPR((SPItem *) items->data);
1909 }
1910 }
1912 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1914 if (n_selected == 0) {
1915 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1916 } else if (n_selected == 1) {
1917 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1919 if (repr) {
1920 g_object_set_data( tbl, "repr", repr );
1921 Inkscape::GC::anchor(repr);
1922 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1923 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1924 }
1925 } else {
1926 // FIXME: implement averaging of all parameters for multiple selected stars
1927 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1928 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1929 }
1930 }
1933 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1934 {
1935 // FIXME: in this and all other _default functions, set some flag telling the value_changed
1936 // callbacks to lump all the changes for all selected objects in one undo step
1938 GtkAdjustment *adj = 0;
1940 // fixme: make settable in prefs!
1941 gint mag = 5;
1942 gdouble prop = 0.5;
1943 gboolean flat = FALSE;
1944 gdouble randomized = 0;
1945 gdouble rounded = 0;
1947 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1948 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1950 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1951 gtk_action_set_sensitive( sb2, !flat );
1953 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1954 gtk_adjustment_set_value(adj, mag);
1955 gtk_adjustment_value_changed(adj);
1957 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1958 gtk_adjustment_set_value(adj, prop);
1959 gtk_adjustment_value_changed(adj);
1961 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1962 gtk_adjustment_set_value(adj, rounded);
1963 gtk_adjustment_value_changed(adj);
1965 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1966 gtk_adjustment_set_value(adj, randomized);
1967 gtk_adjustment_value_changed(adj);
1968 }
1971 void
1972 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1973 {
1974 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1975 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1976 GtkWidget *l = gtk_label_new(NULL);
1977 gtk_label_set_markup(GTK_LABEL(l), title);
1978 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1979 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1980 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1981 }
1984 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1985 {
1986 {
1987 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1988 ege_output_action_set_use_markup( act, TRUE );
1989 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1990 g_object_set_data( holder, "mode_action", act );
1991 }
1993 {
1994 EgeAdjustmentAction* eact = 0;
1995 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1996 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1998 /* Flatsided checkbox */
1999 {
2000 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2002 GtkTreeIter iter;
2003 gtk_list_store_append( model, &iter );
2004 gtk_list_store_set( model, &iter,
2005 0, _("Polygon"),
2006 1, _("Regular polygon (with one handle) instead of a star"),
2007 2, "star_flat",
2008 -1 );
2010 gtk_list_store_append( model, &iter );
2011 gtk_list_store_set( model, &iter,
2012 0, _("Star"),
2013 1, _("Star instead of a regular polygon (with one handle)"),
2014 2, "star_angled",
2015 -1 );
2017 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2018 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2019 g_object_set_data( holder, "flat_action", act );
2021 ege_select_one_action_set_appearance( act, "full" );
2022 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2023 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2024 ege_select_one_action_set_icon_column( act, 2 );
2025 ege_select_one_action_set_tooltip_column( act, 1 );
2027 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2028 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2029 }
2031 /* Magnitude */
2032 {
2033 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2034 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2035 eact = create_adjustment_action( "MagnitudeAction",
2036 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2037 "tools.shapes.star", "magnitude", 3,
2038 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2039 3, 1024, 1, 5,
2040 labels, values, G_N_ELEMENTS(labels),
2041 sp_stb_magnitude_value_changed,
2042 1.0, 0 );
2043 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2044 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2045 }
2047 /* Spoke ratio */
2048 {
2049 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2050 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2051 eact = create_adjustment_action( "SpokeAction",
2052 _("Spoke ratio"), _("Spoke ratio:"),
2053 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2054 // Base radius is the same for the closest handle.
2055 _("Base radius to tip radius ratio"),
2056 "tools.shapes.star", "proportion", 0.5,
2057 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2058 0.01, 1.0, 0.01, 0.1,
2059 labels, values, G_N_ELEMENTS(labels),
2060 sp_stb_proportion_value_changed );
2061 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2062 g_object_set_data( holder, "prop_action", eact );
2063 }
2065 if ( !isFlatSided ) {
2066 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2067 } else {
2068 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2069 }
2071 /* Roundedness */
2072 {
2073 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2074 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2075 eact = create_adjustment_action( "RoundednessAction",
2076 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2077 "tools.shapes.star", "rounded", 0.0,
2078 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2079 -10.0, 10.0, 0.01, 0.1,
2080 labels, values, G_N_ELEMENTS(labels),
2081 sp_stb_rounded_value_changed );
2082 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2083 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2084 }
2086 /* Randomization */
2087 {
2088 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2089 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2090 eact = create_adjustment_action( "RandomizationAction",
2091 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2092 "tools.shapes.star", "randomized", 0.0,
2093 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2094 -10.0, 10.0, 0.001, 0.01,
2095 labels, values, G_N_ELEMENTS(labels),
2096 sp_stb_randomized_value_changed, 0.1, 3 );
2097 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2098 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2099 }
2100 }
2102 {
2103 /* Reset */
2104 {
2105 GtkAction* act = gtk_action_new( "StarResetAction",
2106 _("Defaults"),
2107 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2108 GTK_STOCK_CLEAR );
2109 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2110 gtk_action_group_add_action( mainActions, act );
2111 gtk_action_set_sensitive( act, TRUE );
2112 }
2113 }
2115 sigc::connection *connection = new sigc::connection(
2116 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2117 );
2118 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2119 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2120 }
2123 //########################
2124 //## Rect ##
2125 //########################
2127 static void sp_rtb_sensitivize( GObject *tbl )
2128 {
2129 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2130 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2131 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2133 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2134 gtk_action_set_sensitive( not_rounded, FALSE );
2135 } else {
2136 gtk_action_set_sensitive( not_rounded, TRUE );
2137 }
2138 }
2141 static void
2142 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2143 void (*setter)(SPRect *, gdouble))
2144 {
2145 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2147 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2148 SPUnit const *unit = tracker->getActiveUnit();
2150 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2151 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2152 }
2154 // quit if run by the attr_changed listener
2155 if (g_object_get_data( tbl, "freeze" )) {
2156 return;
2157 }
2159 // in turn, prevent listener from responding
2160 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2162 bool modmade = false;
2163 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2164 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2165 if (SP_IS_RECT(items->data)) {
2166 if (adj->value != 0) {
2167 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2168 } else {
2169 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2170 }
2171 modmade = true;
2172 }
2173 }
2175 sp_rtb_sensitivize( tbl );
2177 if (modmade) {
2178 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2179 _("Change rectangle"));
2180 }
2182 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2183 }
2185 static void
2186 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2187 {
2188 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2189 }
2191 static void
2192 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2193 {
2194 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2195 }
2197 static void
2198 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2199 {
2200 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2201 }
2203 static void
2204 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2205 {
2206 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2207 }
2211 static void
2212 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2213 {
2214 GtkAdjustment *adj = 0;
2216 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2217 gtk_adjustment_set_value(adj, 0.0);
2218 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2219 gtk_adjustment_value_changed(adj);
2221 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2222 gtk_adjustment_set_value(adj, 0.0);
2223 gtk_adjustment_value_changed(adj);
2225 sp_rtb_sensitivize( obj );
2226 }
2228 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2229 gchar const */*old_value*/, gchar const */*new_value*/,
2230 bool /*is_interactive*/, gpointer data)
2231 {
2232 GObject *tbl = G_OBJECT(data);
2234 // quit if run by the _changed callbacks
2235 if (g_object_get_data( tbl, "freeze" )) {
2236 return;
2237 }
2239 // in turn, prevent callbacks from responding
2240 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2242 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2243 SPUnit const *unit = tracker->getActiveUnit();
2245 gpointer item = g_object_get_data( tbl, "item" );
2246 if (item && SP_IS_RECT(item)) {
2247 {
2248 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2249 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2250 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2251 }
2253 {
2254 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2255 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2256 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2257 }
2259 {
2260 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2261 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2262 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2263 }
2265 {
2266 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2267 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2268 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2269 }
2270 }
2272 sp_rtb_sensitivize( tbl );
2274 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2275 }
2278 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2279 NULL, /* child_added */
2280 NULL, /* child_removed */
2281 rect_tb_event_attr_changed,
2282 NULL, /* content_changed */
2283 NULL /* order_changed */
2284 };
2286 /**
2287 * \param selection should not be NULL.
2288 */
2289 static void
2290 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2291 {
2292 int n_selected = 0;
2293 Inkscape::XML::Node *repr = NULL;
2294 SPItem *item = NULL;
2296 if ( g_object_get_data( tbl, "repr" ) ) {
2297 g_object_set_data( tbl, "item", NULL );
2298 }
2299 purge_repr_listener( tbl, tbl );
2301 for (GSList const *items = selection->itemList();
2302 items != NULL;
2303 items = items->next) {
2304 if (SP_IS_RECT((SPItem *) items->data)) {
2305 n_selected++;
2306 item = (SPItem *) items->data;
2307 repr = SP_OBJECT_REPR(item);
2308 }
2309 }
2311 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2313 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2315 if (n_selected == 0) {
2316 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2318 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2319 gtk_action_set_sensitive(w, FALSE);
2320 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2321 gtk_action_set_sensitive(h, FALSE);
2323 } else if (n_selected == 1) {
2324 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2325 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2327 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2328 gtk_action_set_sensitive(w, TRUE);
2329 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2330 gtk_action_set_sensitive(h, TRUE);
2332 if (repr) {
2333 g_object_set_data( tbl, "repr", repr );
2334 g_object_set_data( tbl, "item", item );
2335 Inkscape::GC::anchor(repr);
2336 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2337 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2338 }
2339 } else {
2340 // FIXME: implement averaging of all parameters for multiple selected
2341 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2342 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2343 sp_rtb_sensitivize( tbl );
2344 }
2345 }
2348 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2349 {
2350 EgeAdjustmentAction* eact = 0;
2352 {
2353 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2354 ege_output_action_set_use_markup( act, TRUE );
2355 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2356 g_object_set_data( holder, "mode_action", act );
2357 }
2359 // rx/ry units menu: create
2360 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2361 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2362 // fixme: add % meaning per cent of the width/height
2363 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2364 g_object_set_data( holder, "tracker", tracker );
2366 /* W */
2367 {
2368 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2369 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2370 eact = create_adjustment_action( "RectWidthAction",
2371 _("Width"), _("W:"), _("Width of rectangle"),
2372 "tools.shapes.rect", "width", 0,
2373 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2374 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2375 labels, values, G_N_ELEMENTS(labels),
2376 sp_rtb_width_value_changed );
2377 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2378 g_object_set_data( holder, "width_action", eact );
2379 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2380 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2381 }
2383 /* H */
2384 {
2385 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2386 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2387 eact = create_adjustment_action( "RectHeightAction",
2388 _("Height"), _("H:"), _("Height of rectangle"),
2389 "tools.shapes.rect", "height", 0,
2390 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2391 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2392 labels, values, G_N_ELEMENTS(labels),
2393 sp_rtb_height_value_changed );
2394 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2395 g_object_set_data( holder, "height_action", eact );
2396 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2397 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2398 }
2400 /* rx */
2401 {
2402 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2403 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2404 eact = create_adjustment_action( "RadiusXAction",
2405 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2406 "tools.shapes.rect", "rx", 0,
2407 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2408 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2409 labels, values, G_N_ELEMENTS(labels),
2410 sp_rtb_rx_value_changed);
2411 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2412 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2413 }
2415 /* ry */
2416 {
2417 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2418 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2419 eact = create_adjustment_action( "RadiusYAction",
2420 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2421 "tools.shapes.rect", "ry", 0,
2422 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2423 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2424 labels, values, G_N_ELEMENTS(labels),
2425 sp_rtb_ry_value_changed);
2426 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2427 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2428 }
2430 // add the units menu
2431 {
2432 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2433 gtk_action_group_add_action( mainActions, act );
2434 }
2436 /* Reset */
2437 {
2438 InkAction* inky = ink_action_new( "RectResetAction",
2439 _("Not rounded"),
2440 _("Make corners sharp"),
2441 "squared_corner",
2442 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2443 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2444 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2445 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2446 g_object_set_data( holder, "not_rounded", inky );
2447 }
2449 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2450 sp_rtb_sensitivize( holder );
2452 sigc::connection *connection = new sigc::connection(
2453 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2454 );
2455 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2456 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2457 }
2459 //########################
2460 //## 3D Box ##
2461 //########################
2463 // normalize angle so that it lies in the interval [0,360]
2464 static double box3d_normalize_angle (double a) {
2465 double angle = a + ((int) (a/360.0))*360;
2466 if (angle < 0) {
2467 angle += 360.0;
2468 }
2469 return angle;
2470 }
2472 static void
2473 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2474 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2475 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2476 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2477 // are reset).
2478 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2480 if (is_infinite) {
2481 gtk_toggle_action_set_active(tact, TRUE);
2482 gtk_action_set_sensitive(act, TRUE);
2484 double angle = persp3d_get_infinite_angle(persp, axis);
2485 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2486 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2487 }
2488 } else {
2489 gtk_toggle_action_set_active(tact, FALSE);
2490 gtk_action_set_sensitive(act, FALSE);
2491 }
2492 }
2494 static void
2495 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2496 if (!persp_repr) {
2497 g_print ("No perspective given to box3d_resync_toolbar().\n");
2498 return;
2499 }
2501 GtkWidget *tbl = GTK_WIDGET(data);
2502 GtkAdjustment *adj = 0;
2503 GtkAction *act = 0;
2504 GtkToggleAction *tact = 0;
2505 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2506 {
2507 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2508 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2509 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2511 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2512 }
2513 {
2514 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2515 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2516 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2518 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2519 }
2520 {
2521 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2522 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2523 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2525 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2526 }
2527 }
2529 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2530 gchar const */*old_value*/, gchar const */*new_value*/,
2531 bool /*is_interactive*/, gpointer data)
2532 {
2533 GtkWidget *tbl = GTK_WIDGET(data);
2535 // quit if run by the attr_changed listener
2536 // note: it used to work without the differently called freeze_ attributes (here and in
2537 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2538 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2539 return;
2540 }
2542 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2543 // sp_document_maybe_done() when the document is undo insensitive)
2544 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2546 // TODO: Only update the appropriate part of the toolbar
2547 // if (!strcmp(name, "inkscape:vp_z")) {
2548 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2549 // }
2551 Persp3D *persp = persp3d_get_from_repr(repr);
2552 persp3d_update_box_reprs(persp);
2554 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2555 }
2557 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2558 {
2559 NULL, /* child_added */
2560 NULL, /* child_removed */
2561 box3d_persp_tb_event_attr_changed,
2562 NULL, /* content_changed */
2563 NULL /* order_changed */
2564 };
2566 /**
2567 * \param selection Should not be NULL.
2568 */
2569 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2570 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2571 static void
2572 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2573 {
2574 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2575 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2576 // update the perspectives with infinite VPs and leave the other ones untouched).
2578 Inkscape::XML::Node *persp_repr = NULL;
2579 purge_repr_listener(tbl, tbl);
2581 SPItem *item = selection->singleItem();
2582 if (item && SP_IS_BOX3D(item)) {
2583 // FIXME: Also deal with multiple selected boxes
2584 SPBox3D *box = SP_BOX3D(item);
2585 Persp3D *persp = box3d_get_perspective(box);
2586 persp_repr = SP_OBJECT_REPR(persp);
2587 if (persp_repr) {
2588 g_object_set_data(tbl, "repr", persp_repr);
2589 Inkscape::GC::anchor(persp_repr);
2590 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2591 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2592 }
2594 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2595 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2597 box3d_resync_toolbar(persp_repr, tbl);
2598 }
2599 }
2601 static void
2602 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2603 {
2604 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2605 SPDocument *document = sp_desktop_document(desktop);
2607 // quit if run by the attr_changed listener
2608 // note: it used to work without the differently called freeze_ attributes (here and in
2609 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2610 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2611 return;
2612 }
2614 // in turn, prevent listener from responding
2615 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2617 //Persp3D *persp = document->current_persp3d;
2618 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2619 if (sel_persps.empty()) {
2620 // this can happen when the document is created; we silently ignore it
2621 return;
2622 }
2623 Persp3D *persp = sel_persps.front();
2625 persp->tmat.set_infinite_direction (axis, adj->value);
2626 SP_OBJECT(persp)->updateRepr();
2628 // TODO: use the correct axis here, too
2629 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2631 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2632 }
2635 static void
2636 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2637 {
2638 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2639 }
2641 static void
2642 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2643 {
2644 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2645 }
2647 static void
2648 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2649 {
2650 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2651 }
2654 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2655 {
2656 // TODO: Take all selected perspectives into account
2657 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2658 if (sel_persps.empty()) {
2659 // this can happen when the document is created; we silently ignore it
2660 return;
2661 }
2662 Persp3D *persp = sel_persps.front();
2664 bool set_infinite = gtk_toggle_action_get_active(act);
2665 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2666 }
2668 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2669 {
2670 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2671 }
2673 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2674 {
2675 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2676 }
2678 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2679 {
2680 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2681 }
2683 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2684 {
2685 EgeAdjustmentAction* eact = 0;
2686 SPDocument *document = sp_desktop_document (desktop);
2687 Persp3D *persp = document->current_persp3d;
2689 EgeAdjustmentAction* box3d_angle_x = 0;
2690 EgeAdjustmentAction* box3d_angle_y = 0;
2691 EgeAdjustmentAction* box3d_angle_z = 0;
2693 /* Angle X */
2694 {
2695 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2696 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2697 eact = create_adjustment_action( "3DBoxAngleXAction",
2698 _("Angle in X direction"), _("Angle X:"),
2699 // Translators: PL is short for 'perspective line'
2700 _("Angle of PLs in X direction"),
2701 "tools.shapes.3dbox", "box3d_angle_x", 30,
2702 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2703 -360.0, 360.0, 1.0, 10.0,
2704 labels, values, G_N_ELEMENTS(labels),
2705 box3d_angle_x_value_changed );
2706 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2707 g_object_set_data( holder, "box3d_angle_x_action", eact );
2708 box3d_angle_x = eact;
2709 }
2711 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2712 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2713 } else {
2714 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2715 }
2718 /* VP X state */
2719 {
2720 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2721 // Translators: VP is short for 'vanishing point'
2722 _("State of VP in X direction"),
2723 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2724 "toggle_vp_x",
2725 Inkscape::ICON_SIZE_DECORATION );
2726 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2727 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2728 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2729 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2730 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2731 }
2733 /* Angle Y */
2734 {
2735 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2736 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2737 eact = create_adjustment_action( "3DBoxAngleYAction",
2738 _("Angle in Y direction"), _("Angle Y:"),
2739 // Translators: PL is short for 'perspective line'
2740 _("Angle of PLs in Y direction"),
2741 "tools.shapes.3dbox", "box3d_angle_y", 30,
2742 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2743 -360.0, 360.0, 1.0, 10.0,
2744 labels, values, G_N_ELEMENTS(labels),
2745 box3d_angle_y_value_changed );
2746 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2747 g_object_set_data( holder, "box3d_angle_y_action", eact );
2748 box3d_angle_y = eact;
2749 }
2751 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2752 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2753 } else {
2754 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2755 }
2757 /* VP Y state */
2758 {
2759 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2760 // Translators: VP is short for 'vanishing point'
2761 _("State of VP in Y direction"),
2762 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2763 "toggle_vp_y",
2764 Inkscape::ICON_SIZE_DECORATION );
2765 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2766 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2767 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2768 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2769 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2770 }
2772 /* Angle Z */
2773 {
2774 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2775 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2776 eact = create_adjustment_action( "3DBoxAngleZAction",
2777 _("Angle in Z direction"), _("Angle Z:"),
2778 // Translators: PL is short for 'perspective line'
2779 _("Angle of PLs in Z direction"),
2780 "tools.shapes.3dbox", "box3d_angle_z", 30,
2781 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2782 -360.0, 360.0, 1.0, 10.0,
2783 labels, values, G_N_ELEMENTS(labels),
2784 box3d_angle_z_value_changed );
2785 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2786 g_object_set_data( holder, "box3d_angle_z_action", eact );
2787 box3d_angle_z = eact;
2788 }
2790 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2791 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2792 } else {
2793 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2794 }
2796 /* VP Z state */
2797 {
2798 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2799 // Translators: VP is short for 'vanishing point'
2800 _("State of VP in Z direction"),
2801 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2802 "toggle_vp_z",
2803 Inkscape::ICON_SIZE_DECORATION );
2804 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2805 g_object_set_data( holder, "box3d_vp_z_state_action", act );
2806 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2807 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2808 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2809 }
2811 sigc::connection *connection = new sigc::connection(
2812 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2813 );
2814 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2815 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2816 }
2818 //########################
2819 //## Spiral ##
2820 //########################
2822 static void
2823 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2824 {
2825 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2827 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2828 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2829 }
2831 // quit if run by the attr_changed listener
2832 if (g_object_get_data( tbl, "freeze" )) {
2833 return;
2834 }
2836 // in turn, prevent listener from responding
2837 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2839 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2841 bool modmade = false;
2842 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2843 items != NULL;
2844 items = items->next)
2845 {
2846 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2847 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2848 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2849 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2850 modmade = true;
2851 }
2852 }
2854 g_free(namespaced_name);
2856 if (modmade) {
2857 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2858 _("Change spiral"));
2859 }
2861 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2862 }
2864 static void
2865 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2866 {
2867 sp_spl_tb_value_changed(adj, tbl, "revolution");
2868 }
2870 static void
2871 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2872 {
2873 sp_spl_tb_value_changed(adj, tbl, "expansion");
2874 }
2876 static void
2877 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2878 {
2879 sp_spl_tb_value_changed(adj, tbl, "t0");
2880 }
2882 static void
2883 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2884 {
2885 GtkWidget *tbl = GTK_WIDGET(obj);
2887 GtkAdjustment *adj;
2889 // fixme: make settable
2890 gdouble rev = 5;
2891 gdouble exp = 1.0;
2892 gdouble t0 = 0.0;
2894 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2895 gtk_adjustment_set_value(adj, rev);
2896 gtk_adjustment_value_changed(adj);
2898 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2899 gtk_adjustment_set_value(adj, exp);
2900 gtk_adjustment_value_changed(adj);
2902 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2903 gtk_adjustment_set_value(adj, t0);
2904 gtk_adjustment_value_changed(adj);
2906 spinbutton_defocus(GTK_OBJECT(tbl));
2907 }
2910 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2911 gchar const */*old_value*/, gchar const */*new_value*/,
2912 bool /*is_interactive*/, gpointer data)
2913 {
2914 GtkWidget *tbl = GTK_WIDGET(data);
2916 // quit if run by the _changed callbacks
2917 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2918 return;
2919 }
2921 // in turn, prevent callbacks from responding
2922 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2924 GtkAdjustment *adj;
2925 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2926 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2928 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2929 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2931 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2932 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2934 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2935 }
2938 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2939 NULL, /* child_added */
2940 NULL, /* child_removed */
2941 spiral_tb_event_attr_changed,
2942 NULL, /* content_changed */
2943 NULL /* order_changed */
2944 };
2946 static void
2947 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2948 {
2949 int n_selected = 0;
2950 Inkscape::XML::Node *repr = NULL;
2952 purge_repr_listener( tbl, tbl );
2954 for (GSList const *items = selection->itemList();
2955 items != NULL;
2956 items = items->next)
2957 {
2958 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2959 n_selected++;
2960 repr = SP_OBJECT_REPR((SPItem *) items->data);
2961 }
2962 }
2964 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2966 if (n_selected == 0) {
2967 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2968 } else if (n_selected == 1) {
2969 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2971 if (repr) {
2972 g_object_set_data( tbl, "repr", repr );
2973 Inkscape::GC::anchor(repr);
2974 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2975 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2976 }
2977 } else {
2978 // FIXME: implement averaging of all parameters for multiple selected
2979 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2980 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2981 }
2982 }
2985 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2986 {
2987 EgeAdjustmentAction* eact = 0;
2989 {
2990 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2991 ege_output_action_set_use_markup( act, TRUE );
2992 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2993 g_object_set_data( holder, "mode_action", act );
2994 }
2996 /* Revolution */
2997 {
2998 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2999 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3000 eact = create_adjustment_action( "SpiralRevolutionAction",
3001 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3002 "tools.shapes.spiral", "revolution", 3.0,
3003 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3004 0.01, 1024.0, 0.1, 1.0,
3005 labels, values, G_N_ELEMENTS(labels),
3006 sp_spl_tb_revolution_value_changed, 1, 2);
3007 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3008 }
3010 /* Expansion */
3011 {
3012 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3013 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3014 eact = create_adjustment_action( "SpiralExpansionAction",
3015 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3016 "tools.shapes.spiral", "expansion", 1.0,
3017 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3018 0.0, 1000.0, 0.01, 1.0,
3019 labels, values, G_N_ELEMENTS(labels),
3020 sp_spl_tb_expansion_value_changed);
3021 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3022 }
3024 /* T0 */
3025 {
3026 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3027 gdouble values[] = {0, 0.5, 0.9};
3028 eact = create_adjustment_action( "SpiralT0Action",
3029 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3030 "tools.shapes.spiral", "t0", 0.0,
3031 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3032 0.0, 0.999, 0.01, 1.0,
3033 labels, values, G_N_ELEMENTS(labels),
3034 sp_spl_tb_t0_value_changed);
3035 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3036 }
3038 /* Reset */
3039 {
3040 InkAction* inky = ink_action_new( "SpiralResetAction",
3041 _("Defaults"),
3042 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3043 GTK_STOCK_CLEAR,
3044 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3045 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3046 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3047 }
3050 sigc::connection *connection = new sigc::connection(
3051 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3052 );
3053 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3054 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3055 }
3057 //########################
3058 //## Pen/Pencil ##
3059 //########################
3062 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
3063 {
3064 // Put stuff here
3065 }
3067 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
3068 {
3069 // Put stuff here
3070 }
3072 //########################
3073 //## Tweak ##
3074 //########################
3076 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3077 {
3078 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3079 }
3081 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3082 {
3083 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3084 }
3086 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3087 {
3088 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3089 }
3091 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3092 {
3093 int mode = ege_select_one_action_get_active( act );
3094 prefs_set_int_attribute("tools.tweak", "mode", mode);
3096 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3097 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3098 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3099 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3100 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3101 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3102 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3103 if (doh) gtk_action_set_sensitive (doh, TRUE);
3104 if (dos) gtk_action_set_sensitive (dos, TRUE);
3105 if (dol) gtk_action_set_sensitive (dol, TRUE);
3106 if (doo) gtk_action_set_sensitive (doo, TRUE);
3107 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3108 if (fid) gtk_action_set_sensitive (fid, FALSE);
3109 } else {
3110 if (doh) gtk_action_set_sensitive (doh, FALSE);
3111 if (dos) gtk_action_set_sensitive (dos, FALSE);
3112 if (dol) gtk_action_set_sensitive (dol, FALSE);
3113 if (doo) gtk_action_set_sensitive (doo, FALSE);
3114 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3115 if (fid) gtk_action_set_sensitive (fid, TRUE);
3116 }
3117 }
3119 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3120 {
3121 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3122 }
3124 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3125 bool show = gtk_toggle_action_get_active( act );
3126 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3127 }
3128 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3129 bool show = gtk_toggle_action_get_active( act );
3130 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3131 }
3132 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3133 bool show = gtk_toggle_action_get_active( act );
3134 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3135 }
3136 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3137 bool show = gtk_toggle_action_get_active( act );
3138 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3139 }
3141 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3142 {
3143 {
3144 /* Width */
3145 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3146 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3147 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3148 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3149 "tools.tweak", "width", 15,
3150 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3151 1, 100, 1.0, 10.0,
3152 labels, values, G_N_ELEMENTS(labels),
3153 sp_tweak_width_value_changed, 0.01, 0, 100 );
3154 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3155 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3156 }
3159 {
3160 /* Force */
3161 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3162 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3163 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3164 _("Force"), _("Force:"), _("The force of the tweak action"),
3165 "tools.tweak", "force", 20,
3166 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3167 1, 100, 1.0, 10.0,
3168 labels, values, G_N_ELEMENTS(labels),
3169 sp_tweak_force_value_changed, 0.01, 0, 100 );
3170 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3171 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3172 }
3174 /* Mode */
3175 {
3176 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3178 GtkTreeIter iter;
3179 gtk_list_store_append( model, &iter );
3180 gtk_list_store_set( model, &iter,
3181 0, _("Push mode"),
3182 1, _("Push parts of paths in any direction"),
3183 2, "tweak_push_mode",
3184 -1 );
3186 gtk_list_store_append( model, &iter );
3187 gtk_list_store_set( model, &iter,
3188 0, _("Shrink mode"),
3189 1, _("Shrink (inset) parts of paths"),
3190 2, "tweak_shrink_mode",
3191 -1 );
3193 gtk_list_store_append( model, &iter );
3194 gtk_list_store_set( model, &iter,
3195 0, _("Grow mode"),
3196 1, _("Grow (outset) parts of paths"),
3197 2, "tweak_grow_mode",
3198 -1 );
3200 gtk_list_store_append( model, &iter );
3201 gtk_list_store_set( model, &iter,
3202 0, _("Attract mode"),
3203 1, _("Attract parts of paths towards cursor"),
3204 2, "tweak_attract_mode",
3205 -1 );
3207 gtk_list_store_append( model, &iter );
3208 gtk_list_store_set( model, &iter,
3209 0, _("Repel mode"),
3210 1, _("Repel parts of paths from cursor"),
3211 2, "tweak_repel_mode",
3212 -1 );
3214 gtk_list_store_append( model, &iter );
3215 gtk_list_store_set( model, &iter,
3216 0, _("Roughen mode"),
3217 1, _("Roughen parts of paths"),
3218 2, "tweak_roughen_mode",
3219 -1 );
3221 gtk_list_store_append( model, &iter );
3222 gtk_list_store_set( model, &iter,
3223 0, _("Color paint mode"),
3224 1, _("Paint the tool's color upon selected objects"),
3225 2, "tweak_colorpaint_mode",
3226 -1 );
3228 gtk_list_store_append( model, &iter );
3229 gtk_list_store_set( model, &iter,
3230 0, _("Color jitter mode"),
3231 1, _("Jitter the colors of selected objects"),
3232 2, "tweak_colorjitter_mode",
3233 -1 );
3235 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3236 g_object_set( act, "short_label", _("Mode:"), NULL );
3237 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3238 g_object_set_data( holder, "mode_action", act );
3240 ege_select_one_action_set_appearance( act, "full" );
3241 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3242 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3243 ege_select_one_action_set_icon_column( act, 2 );
3244 ege_select_one_action_set_tooltip_column( act, 1 );
3246 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3247 ege_select_one_action_set_active( act, mode );
3248 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3250 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3251 }
3253 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3255 {
3256 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3257 ege_output_action_set_use_markup( act, TRUE );
3258 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3259 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3260 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3261 g_object_set_data( holder, "tweak_channels_label", act);
3262 }
3264 {
3265 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3266 _("Hue"),
3267 _("In color mode, act on objects' hue"),
3268 NULL,
3269 Inkscape::ICON_SIZE_DECORATION );
3270 g_object_set( act, "short_label", _("H"), NULL );
3271 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3272 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3273 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3274 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3275 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3276 g_object_set_data( holder, "tweak_doh", act);
3277 }
3278 {
3279 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3280 _("Saturation"),
3281 _("In color mode, act on objects' saturation"),
3282 NULL,
3283 Inkscape::ICON_SIZE_DECORATION );
3284 g_object_set( act, "short_label", _("S"), NULL );
3285 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3286 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3287 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3288 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3289 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3290 g_object_set_data( holder, "tweak_dos", act );
3291 }
3292 {
3293 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3294 _("Lightness"),
3295 _("In color mode, act on objects' lightness"),
3296 NULL,
3297 Inkscape::ICON_SIZE_DECORATION );
3298 g_object_set( act, "short_label", _("L"), NULL );
3299 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3300 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3301 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3302 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3303 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3304 g_object_set_data( holder, "tweak_dol", act );
3305 }
3306 {
3307 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3308 _("Opacity"),
3309 _("In color mode, act on objects' opacity"),
3310 NULL,
3311 Inkscape::ICON_SIZE_DECORATION );
3312 g_object_set( act, "short_label", _("O"), NULL );
3313 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3314 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3315 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3316 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3317 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3318 g_object_set_data( holder, "tweak_doo", act );
3319 }
3321 { /* Fidelity */
3322 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3323 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3324 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3325 _("Fidelity"), _("Fidelity:"),
3326 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3327 "tools.tweak", "fidelity", 50,
3328 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3329 1, 100, 1.0, 10.0,
3330 labels, values, G_N_ELEMENTS(labels),
3331 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3332 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3333 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3334 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3335 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3336 g_object_set_data( holder, "tweak_fidelity", eact );
3337 }
3340 /* Use Pressure button */
3341 {
3342 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3343 _("Pressure"),
3344 _("Use the pressure of the input device to alter the force of tweak action"),
3345 "use_pressure",
3346 Inkscape::ICON_SIZE_DECORATION );
3347 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3348 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3349 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3350 }
3352 }
3355 //########################
3356 //## Calligraphy ##
3357 //########################
3358 static void update_presets_list(GObject *dataKludge ){
3359 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3360 if (sel) {
3361 ege_select_one_action_set_active(sel, 0 );
3362 }
3363 }
3365 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3366 {
3367 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3368 update_presets_list(tbl);
3369 }
3371 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3372 {
3373 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3374 update_presets_list(tbl);
3375 }
3377 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3378 {
3379 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3380 update_presets_list(tbl);
3381 }
3383 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3384 {
3385 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3386 update_presets_list(tbl);
3387 }
3389 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3390 {
3391 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3392 update_presets_list(tbl);
3393 }
3395 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3396 {
3397 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3398 update_presets_list(tbl);
3399 }
3401 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3402 {
3403 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3404 update_presets_list(tbl);
3405 }
3407 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3408 {
3409 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3410 update_presets_list(tbl);
3411 }
3413 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3414 {
3415 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3416 update_presets_list(tbl);
3417 }
3419 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
3420 {
3421 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3422 update_presets_list(tbl);
3423 }
3425 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
3426 {
3427 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3428 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3429 update_presets_list(tbl);
3430 if (calligraphy_angle )
3431 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3432 }
3435 #define PROFILE_FLOAT_SIZE 8
3436 #define PROFILE_BOOL_SIZE 3
3437 struct ProfileFloatElement {
3438 char const *name;
3439 double def;
3440 double min;
3441 double max;
3442 };
3443 struct ProfileBoolElement {
3444 char const *name;
3445 bool def;
3446 };
3448 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3449 {"mass",0.02, 0.0, 1.0},
3450 {"wiggle",0.0, 0.0, 1.0},
3451 {"angle",30.0, -90.0, 90.0},
3452 {"width",15.0, 1.0, 100.0},
3453 {"thinning",0.1, -1.0, 1.0},
3454 {"tremor",0.0, 0.0, 1.0},
3455 {"flatness",0.9, 0.0, 1.0},
3456 {"cap_rounding",0.0, 0.0, 5.0}
3457 };
3458 static ProfileBoolElement b_profile[PROFILE_BOOL_SIZE] = {
3459 {"usepressure",true},
3460 {"tracebackground",false},
3461 {"usetilt",true},
3462 };
3466 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3468 unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset");
3469 gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3470 gchar *profile_name = g_strdup_printf("Profile %d", new_index);
3471 gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);
3473 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3474 ProfileFloatElement const &pe = f_profile[i];
3475 double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3476 prefs_set_double_attribute(pref_path,pe.name,v);
3477 }
3478 for (unsigned i = 0; i < PROFILE_BOOL_SIZE; ++i) {
3479 ProfileBoolElement const &pe = b_profile[i];
3480 int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def?1:0 ,0,1);
3481 prefs_set_int_attribute(pref_path,pe.name,v);
3482 }
3483 prefs_set_string_attribute(pref_path,"name",profile_name);
3485 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3486 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3487 GtkTreeIter iter;
3488 gtk_list_store_append( model, &iter );
3489 gtk_list_store_set( model, &iter, 0, profile_name, 1, new_index, -1 );
3491 free(profile_id);
3492 free(profile_name);
3493 free(pref_path);
3494 }
3496 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3498 gint preset_index = ege_select_one_action_get_active( act );
3499 gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3501 if ( profile_name) {
3502 g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3503 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3504 ProfileFloatElement const &pe = f_profile[i];
3505 double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3506 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3507 if ( adj ) {
3508 gtk_adjustment_set_value(adj, v);
3509 }
3510 }
3511 for (unsigned i = 0; i < PROFILE_BOOL_SIZE; ++i) {
3512 ProfileBoolElement const &pe = b_profile[i];
3513 int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, 0, 1);
3514 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3515 if ( toggle ) {
3516 gtk_toggle_action_set_active(toggle, v);
3517 } else printf("No toggle");
3518 }
3519 free(profile_name);
3520 g_object_set_data(dataKludge, "profile_selector",act); //restor selector visibility
3521 }
3523 }
3526 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3527 {
3528 {
3529 EgeAdjustmentAction* calligraphy_angle = 0;
3531 {
3532 /* Width */
3533 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3534 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3535 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3536 _("Pen Width"), _("Width:"),
3537 _("The width of the calligraphic pen (relative to the visible canvas area)"),
3538 "tools.calligraphic", "width", 15,
3539 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3540 1, 100, 1.0, 10.0,
3541 labels, values, G_N_ELEMENTS(labels),
3542 sp_ddc_width_value_changed, 0.01, 0, 100 );
3543 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3544 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3545 }
3547 {
3548 /* Thinning */
3549 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3550 gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3551 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3552 _("Stroke Thinning"), _("Thinning:"),
3553 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3554 "tools.calligraphic", "thinning", 0.1,
3555 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3556 -1.0, 1.0, 0.01, 0.1,
3557 labels, values, G_N_ELEMENTS(labels),
3558 sp_ddc_velthin_value_changed, 0.01, 2);
3559 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3560 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3561 }
3563 {
3564 /* Angle */
3565 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3566 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3567 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3568 _("Pen Angle"), _("Angle:"),
3569 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3570 "tools.calligraphic", "angle", 30,
3571 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3572 -90.0, 90.0, 1.0, 10.0,
3573 labels, values, G_N_ELEMENTS(labels),
3574 sp_ddc_angle_value_changed, 1, 0 );
3575 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3576 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3577 g_object_set_data( holder, "angle", eact );
3578 calligraphy_angle = eact;
3579 }
3581 {
3582 /* Fixation */
3583 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3584 gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3585 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3586 _("Fixation"), _("Fixation:"),
3587 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3588 "tools.calligraphic", "flatness", 0.9,
3589 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3590 0.0, 1.0, 0.01, 0.1,
3591 labels, values, G_N_ELEMENTS(labels),
3592 sp_ddc_flatness_value_changed, 0.01, 2 );
3593 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3594 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3595 }
3597 {
3598 /* Cap Rounding */
3599 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3600 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3601 // TRANSLATORS: "cap" means "end" (both start and finish) here
3602 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3603 _("Cap rounding"), _("Caps:"),
3604 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3605 "tools.calligraphic", "cap_rounding", 0.0,
3606 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3607 0.0, 5.0, 0.01, 0.1,
3608 labels, values, G_N_ELEMENTS(labels),
3609 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3610 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3611 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3612 }
3614 {
3615 /* Tremor */
3616 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3617 gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3618 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3619 _("Stroke Tremor"), _("Tremor:"),
3620 _("Increase to make strokes rugged and trembling"),
3621 "tools.calligraphic", "tremor", 0.0,
3622 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3623 0.0, 1.0, 0.01, 0.1,
3624 labels, values, G_N_ELEMENTS(labels),
3625 sp_ddc_tremor_value_changed, 0.01, 2 );
3627 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3628 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3629 }
3631 {
3632 /* Wiggle */
3633 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3634 gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3635 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3636 _("Pen Wiggle"), _("Wiggle:"),
3637 _("Increase to make the pen waver and wiggle"),
3638 "tools.calligraphic", "wiggle", 0.0,
3639 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3640 0.0, 1.0, 0.01, 0.1,
3641 labels, values, G_N_ELEMENTS(labels),
3642 sp_ddc_wiggle_value_changed, 0.01, 2 );
3643 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3644 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3645 }
3647 {
3648 /* Mass */
3649 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3650 gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3651 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3652 _("Pen Mass"), _("Mass:"),
3653 _("Increase to make the pen drag behind, as if slowed by inertia"),
3654 "tools.calligraphic", "mass", 0.02,
3655 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3656 0.0, 1.0, 0.01, 0.1,
3657 labels, values, G_N_ELEMENTS(labels),
3658 sp_ddc_mass_value_changed, 0.01, 2 );
3659 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3660 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3661 }
3664 /* Trace Background button */
3665 {
3666 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3667 _("Trace Background"),
3668 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3669 "trace_background",
3670 Inkscape::ICON_SIZE_DECORATION );
3671 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3672 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
3673 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3674 g_object_set_data( holder, "tracebackground", act );
3675 }
3677 /* Use Pressure button */
3678 {
3679 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3680 _("Pressure"),
3681 _("Use the pressure of the input device to alter the width of the pen"),
3682 "use_pressure",
3683 Inkscape::ICON_SIZE_DECORATION );
3684 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3685 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
3686 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3687 g_object_set_data( holder, "usepressure", act );
3688 }
3690 /* Use Tilt button */
3691 {
3692 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3693 _("Tilt"),
3694 _("Use the tilt of the input device to alter the angle of the pen's nib"),
3695 "use_tilt",
3696 Inkscape::ICON_SIZE_DECORATION );
3697 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3698 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
3699 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3700 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3701 g_object_set_data( holder, "usetilt", act );
3702 }
3704 /*calligraphic profile */
3705 {
3706 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3707 gchar *pref_path;
3710 GtkTreeIter iter;
3711 gtk_list_store_append( model, &iter );
3712 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3714 //TODO: switch back to prefs API
3715 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3716 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3717 int ii=1;
3718 while (child_repr) {
3719 GtkTreeIter iter;
3720 char *preset_name = (char *) child_repr->attribute("name");
3721 gtk_list_store_append( model, &iter );
3722 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
3723 child_repr = sp_repr_next(child_repr);
3724 }
3726 pref_path = NULL;
3727 EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , ("Change calligraphic profile"), NULL, GTK_TREE_MODEL(model) );
3728 ege_select_one_action_set_appearance( act1, "compact" );
3729 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
3730 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3731 g_object_set_data( holder, "profile_selector", act1 );
3732 }
3734 /*Save or delete calligraphic profile */
3735 {
3736 GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
3737 _("Defaults"),
3738 _("Save current settings as new profile"),
3739 GTK_STOCK_SAVE );
3740 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
3741 gtk_action_group_add_action( mainActions, act );
3742 gtk_action_set_sensitive( act, TRUE );
3743 g_object_set_data( holder, "profile_save_delete", act );
3744 }
3745 }
3746 }
3749 //########################
3750 //## Circle / Arc ##
3751 //########################
3753 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3754 {
3755 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3756 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3758 if (v1 == 0 && v2 == 0) {
3759 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3760 gtk_action_set_sensitive( ocb, FALSE );
3761 gtk_action_set_sensitive( make_whole, FALSE );
3762 }
3763 } else {
3764 gtk_action_set_sensitive( ocb, TRUE );
3765 gtk_action_set_sensitive( make_whole, TRUE );
3766 }
3767 }
3769 static void
3770 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3771 {
3772 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3774 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3775 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3776 }
3778 // quit if run by the attr_changed listener
3779 if (g_object_get_data( tbl, "freeze" )) {
3780 return;
3781 }
3783 // in turn, prevent listener from responding
3784 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3786 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3788 bool modmade = false;
3789 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3790 items != NULL;
3791 items = items->next)
3792 {
3793 SPItem *item = SP_ITEM(items->data);
3795 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3797 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3798 SPArc *arc = SP_ARC(item);
3800 if (!strcmp(value_name, "start"))
3801 ge->start = (adj->value * M_PI)/ 180;
3802 else
3803 ge->end = (adj->value * M_PI)/ 180;
3805 sp_genericellipse_normalize(ge);
3806 ((SPObject *)arc)->updateRepr();
3807 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3809 modmade = true;
3810 }
3811 }
3813 g_free(namespaced_name);
3815 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3817 sp_arctb_sensitivize( tbl, adj->value, other->value );
3819 if (modmade) {
3820 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3821 _("Arc: Change start/end"));
3822 }
3824 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3825 }
3828 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
3829 {
3830 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
3831 }
3833 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3834 {
3835 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
3836 }
3838 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3839 {
3840 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3841 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3842 if ( ege_select_one_action_get_active( act ) != 0 ) {
3843 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3844 } else {
3845 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3846 }
3847 }
3849 // quit if run by the attr_changed listener
3850 if (g_object_get_data( tbl, "freeze" )) {
3851 return;
3852 }
3854 // in turn, prevent listener from responding
3855 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3857 bool modmade = false;
3859 if ( ege_select_one_action_get_active(act) != 0 ) {
3860 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3861 items != NULL;
3862 items = items->next)
3863 {
3864 if (SP_IS_ARC((SPItem *) items->data)) {
3865 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3866 repr->setAttribute("sodipodi:open", "true");
3867 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3868 modmade = true;
3869 }
3870 }
3871 } else {
3872 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3873 items != NULL;
3874 items = items->next)
3875 {
3876 if (SP_IS_ARC((SPItem *) items->data)) {
3877 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3878 repr->setAttribute("sodipodi:open", NULL);
3879 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3880 modmade = true;
3881 }
3882 }
3883 }
3885 if (modmade) {
3886 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3887 _("Arc: Change open/closed"));
3888 }
3890 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3891 }
3893 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3894 {
3895 GtkAdjustment *adj;
3896 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3897 gtk_adjustment_set_value(adj, 0.0);
3898 gtk_adjustment_value_changed(adj);
3900 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3901 gtk_adjustment_set_value(adj, 0.0);
3902 gtk_adjustment_value_changed(adj);
3904 spinbutton_defocus( GTK_OBJECT(obj) );
3905 }
3907 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3908 gchar const */*old_value*/, gchar const */*new_value*/,
3909 bool /*is_interactive*/, gpointer data)
3910 {
3911 GObject *tbl = G_OBJECT(data);
3913 // quit if run by the _changed callbacks
3914 if (g_object_get_data( tbl, "freeze" )) {
3915 return;
3916 }
3918 // in turn, prevent callbacks from responding
3919 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3921 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3922 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3924 GtkAdjustment *adj1,*adj2;
3925 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3926 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3927 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3928 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3930 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3932 char const *openstr = NULL;
3933 openstr = repr->attribute("sodipodi:open");
3934 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3936 if (openstr) {
3937 ege_select_one_action_set_active( ocb, 1 );
3938 } else {
3939 ege_select_one_action_set_active( ocb, 0 );
3940 }
3942 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3943 }
3945 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3946 NULL, /* child_added */
3947 NULL, /* child_removed */
3948 arc_tb_event_attr_changed,
3949 NULL, /* content_changed */
3950 NULL /* order_changed */
3951 };
3954 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3955 {
3956 int n_selected = 0;
3957 Inkscape::XML::Node *repr = NULL;
3959 purge_repr_listener( tbl, tbl );
3961 for (GSList const *items = selection->itemList();
3962 items != NULL;
3963 items = items->next)
3964 {
3965 if (SP_IS_ARC((SPItem *) items->data)) {
3966 n_selected++;
3967 repr = SP_OBJECT_REPR((SPItem *) items->data);
3968 }
3969 }
3971 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3973 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3974 if (n_selected == 0) {
3975 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3976 } else if (n_selected == 1) {
3977 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3978 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3980 if (repr) {
3981 g_object_set_data( tbl, "repr", repr );
3982 Inkscape::GC::anchor(repr);
3983 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3984 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3985 }
3986 } else {
3987 // FIXME: implement averaging of all parameters for multiple selected
3988 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3989 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3990 sp_arctb_sensitivize( tbl, 1, 0 );
3991 }
3992 }
3995 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3996 {
3997 EgeAdjustmentAction* eact = 0;
4000 {
4001 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4002 ege_output_action_set_use_markup( act, TRUE );
4003 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4004 g_object_set_data( holder, "mode_action", act );
4005 }
4007 /* Start */
4008 {
4009 eact = create_adjustment_action( "ArcStartAction",
4010 _("Start"), _("Start:"),
4011 _("The angle (in degrees) from the horizontal to the arc's start point"),
4012 "tools.shapes.arc", "start", 0.0,
4013 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4014 -360.0, 360.0, 1.0, 10.0,
4015 0, 0, 0,
4016 sp_arctb_start_value_changed);
4017 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4018 }
4020 /* End */
4021 {
4022 eact = create_adjustment_action( "ArcEndAction",
4023 _("End"), _("End:"),
4024 _("The angle (in degrees) from the horizontal to the arc's end point"),
4025 "tools.shapes.arc", "end", 0.0,
4026 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4027 -360.0, 360.0, 1.0, 10.0,
4028 0, 0, 0,
4029 sp_arctb_end_value_changed);
4030 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4031 }
4033 /* Segments / Pie checkbox */
4034 {
4035 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4037 GtkTreeIter iter;
4038 gtk_list_store_append( model, &iter );
4039 gtk_list_store_set( model, &iter,
4040 0, _("Closed arc"),
4041 1, _("Switch to segment (closed shape with two radii)"),
4042 2, "circle_closed_arc",
4043 -1 );
4045 gtk_list_store_append( model, &iter );
4046 gtk_list_store_set( model, &iter,
4047 0, _("Open Arc"),
4048 1, _("Switch to arc (unclosed shape)"),
4049 2, "circle_open_arc",
4050 -1 );
4052 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4053 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4054 g_object_set_data( holder, "open_action", act );
4056 ege_select_one_action_set_appearance( act, "full" );
4057 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4058 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4059 ege_select_one_action_set_icon_column( act, 2 );
4060 ege_select_one_action_set_tooltip_column( act, 1 );
4062 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4063 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4064 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4065 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4066 }
4068 /* Make Whole */
4069 {
4070 InkAction* inky = ink_action_new( "ArcResetAction",
4071 _("Make whole"),
4072 _("Make the shape a whole ellipse, not arc or segment"),
4073 "reset_circle",
4074 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4075 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4076 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4077 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4078 g_object_set_data( holder, "make_whole", inky );
4079 }
4081 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4082 // sensitivize make whole and open checkbox
4083 {
4084 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4085 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4086 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4087 }
4090 sigc::connection *connection = new sigc::connection(
4091 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4092 );
4093 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4094 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4095 }
4100 // toggle button callbacks and updaters
4102 //########################
4103 //## Dropper ##
4104 //########################
4106 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4107 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4108 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4109 if ( set_action ) {
4110 if ( gtk_toggle_action_get_active( act ) ) {
4111 gtk_action_set_sensitive( set_action, TRUE );
4112 } else {
4113 gtk_action_set_sensitive( set_action, FALSE );
4114 }
4115 }
4117 spinbutton_defocus(GTK_OBJECT(tbl));
4118 }
4120 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4121 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4122 spinbutton_defocus(GTK_OBJECT(tbl));
4123 }
4126 /**
4127 * Dropper auxiliary toolbar construction and setup.
4128 *
4129 * TODO: Would like to add swatch of current color.
4130 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4131 * can drag and drop places. Will provide a nice mixing palette.
4132 */
4133 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4134 {
4135 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4137 {
4138 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4139 ege_output_action_set_use_markup( act, TRUE );
4140 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4141 }
4143 {
4144 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4145 _("Pick opacity"),
4146 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4147 NULL,
4148 Inkscape::ICON_SIZE_DECORATION );
4149 g_object_set( act, "short_label", _("Pick"), NULL );
4150 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4151 g_object_set_data( holder, "pick_action", act );
4152 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4153 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4154 }
4156 {
4157 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4158 _("Assign opacity"),
4159 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4160 NULL,
4161 Inkscape::ICON_SIZE_DECORATION );
4162 g_object_set( act, "short_label", _("Assign"), NULL );
4163 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4164 g_object_set_data( holder, "set_action", act );
4165 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4166 // make sure it's disabled if we're not picking alpha
4167 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4168 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4169 }
4170 }
4173 //########################
4174 //## Text Toolbox ##
4175 //########################
4176 /*
4177 static void
4178 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4179 {
4180 //Call back for letter sizing spinbutton
4181 }
4183 static void
4184 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4185 {
4186 //Call back for line height spinbutton
4187 }
4189 static void
4190 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4191 {
4192 //Call back for horizontal kerning spinbutton
4193 }
4195 static void
4196 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4197 {
4198 //Call back for vertical kerning spinbutton
4199 }
4201 static void
4202 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4203 {
4204 //Call back for letter rotation spinbutton
4205 }*/
4207 namespace {
4209 bool popdown_visible = false;
4210 bool popdown_hasfocus = false;
4212 void
4213 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4214 {
4215 SPStyle *query =
4216 sp_style_new (SP_ACTIVE_DOCUMENT);
4218 int result_fontspec =
4219 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4221 int result_family =
4222 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4224 int result_style =
4225 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4227 int result_numbers =
4228 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4230 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4232 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4233 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4234 {
4235 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4237 if (repr)
4238 {
4239 sp_style_read_from_repr (query, repr);
4240 }
4241 else
4242 {
4243 return;
4244 }
4245 }
4247 if (query->text)
4248 {
4249 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4250 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4251 gtk_entry_set_text (GTK_ENTRY (entry), "");
4253 } else if (query->text->font_specification.value || query->text->font_family.value) {
4255 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4257 // Get the font that corresponds
4258 Glib::ustring familyName;
4260 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4261 if (font) {
4262 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4263 font->Unref();
4264 font = NULL;
4265 }
4267 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4269 Gtk::TreePath path;
4270 try {
4271 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4272 } catch (...) {
4273 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4274 return;
4275 }
4277 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4278 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4280 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4282 gtk_tree_selection_select_path (tselection, path.gobj());
4283 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4285 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4286 }
4288 //Size
4289 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4290 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4291 g_object_set_data (tbl, "size-block", gpointer(1));
4292 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4293 g_object_set_data (tbl, "size-block", gpointer(0));
4294 free (str);
4296 //Anchor
4297 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4298 {
4299 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4300 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4301 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4302 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4303 }
4304 else
4305 {
4306 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4307 {
4308 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4309 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4310 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4311 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4312 }
4313 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4314 {
4315 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4316 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4317 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4318 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4319 }
4320 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4321 {
4322 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4323 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4324 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4325 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4326 }
4327 }
4329 //Style
4330 {
4331 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4333 gboolean active = gtk_toggle_button_get_active (button);
4334 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4336 if (active != check)
4337 {
4338 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4339 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4340 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4341 }
4342 }
4344 {
4345 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4347 gboolean active = gtk_toggle_button_get_active (button);
4348 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4350 if (active != check)
4351 {
4352 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4353 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4354 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4355 }
4356 }
4358 //Orientation
4359 //locking both buttons, changing one affect all group (both)
4360 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4361 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4363 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4364 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4366 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4367 {
4368 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4369 }
4370 else
4371 {
4372 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4373 }
4374 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4375 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4376 }
4378 sp_style_unref(query);
4379 }
4381 void
4382 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4383 {
4384 sp_text_toolbox_selection_changed (selection, tbl);
4385 }
4387 void
4388 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4389 {
4390 sp_text_toolbox_selection_changed (NULL, tbl);
4391 }
4393 void
4394 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
4395 GObject *tbl)
4396 {
4397 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4398 GtkTreeModel *model = 0;
4399 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4400 GtkTreeIter iter;
4401 char *family = 0;
4403 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4404 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4406 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4407 return;
4408 }
4410 gtk_tree_model_get (model, &iter, 0, &family, -1);
4412 if (g_object_get_data (G_OBJECT (selection), "block"))
4413 {
4414 gtk_entry_set_text (GTK_ENTRY (entry), family);
4415 return;
4416 }
4418 gtk_entry_set_text (GTK_ENTRY (entry), family);
4420 SPStyle *query =
4421 sp_style_new (SP_ACTIVE_DOCUMENT);
4423 int result_fontspec =
4424 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4426 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4428 SPCSSAttr *css = sp_repr_css_attr_new ();
4431 // First try to get the font spec from the stored value
4432 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4434 if (fontSpec.empty()) {
4435 // Construct a new font specification if it does not yet exist
4436 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4437 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4438 fontFromStyle->Unref();
4439 }
4441 if (!fontSpec.empty()) {
4442 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4443 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4444 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4445 if (font) {
4446 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4448 // Set all the these just in case they were altered when finding the best
4449 // match for the new family and old style...
4451 gchar c[256];
4453 font->Family(c, 256);
4454 sp_repr_css_set_property (css, "font-family", c);
4456 font->Attribute( "weight", c, 256);
4457 sp_repr_css_set_property (css, "font-weight", c);
4459 font->Attribute("style", c, 256);
4460 sp_repr_css_set_property (css, "font-style", c);
4462 font->Attribute("stretch", c, 256);
4463 sp_repr_css_set_property (css, "font-stretch", c);
4465 font->Attribute("variant", c, 256);
4466 sp_repr_css_set_property (css, "font-variant", c);
4468 font->Unref();
4469 }
4470 }
4471 }
4473 // If querying returned nothing, set the default style of the tool (for new texts)
4474 if (result_fontspec == QUERY_STYLE_NOTHING)
4475 {
4476 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4477 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4478 }
4479 else
4480 {
4481 sp_desktop_set_style (desktop, css, true, true);
4482 }
4484 sp_style_unref(query);
4486 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4487 _("Text: Change font family"));
4488 sp_repr_css_attr_unref (css);
4489 free (family);
4490 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4492 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4493 }
4495 void
4496 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
4497 GObject *tbl)
4498 {
4499 const char *family = gtk_entry_get_text (entry);
4501 try {
4502 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4503 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4504 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4505 gtk_tree_selection_select_path (selection, path.gobj());
4506 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4507 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4508 } catch (...) {
4509 if (family && strlen (family))
4510 {
4511 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4512 }
4513 }
4514 }
4516 void
4517 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
4518 gpointer data)
4519 {
4520 if (g_object_get_data (G_OBJECT (button), "block")) return;
4521 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4522 int prop = GPOINTER_TO_INT(data);
4524 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4525 SPCSSAttr *css = sp_repr_css_attr_new ();
4527 switch (prop)
4528 {
4529 case 0:
4530 {
4531 sp_repr_css_set_property (css, "text-anchor", "start");
4532 sp_repr_css_set_property (css, "text-align", "start");
4533 break;
4534 }
4535 case 1:
4536 {
4537 sp_repr_css_set_property (css, "text-anchor", "middle");
4538 sp_repr_css_set_property (css, "text-align", "center");
4539 break;
4540 }
4542 case 2:
4543 {
4544 sp_repr_css_set_property (css, "text-anchor", "end");
4545 sp_repr_css_set_property (css, "text-align", "end");
4546 break;
4547 }
4549 case 3:
4550 {
4551 sp_repr_css_set_property (css, "text-anchor", "start");
4552 sp_repr_css_set_property (css, "text-align", "justify");
4553 break;
4554 }
4555 }
4557 SPStyle *query =
4558 sp_style_new (SP_ACTIVE_DOCUMENT);
4559 int result_numbers =
4560 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4562 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4563 if (result_numbers == QUERY_STYLE_NOTHING)
4564 {
4565 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4566 }
4568 sp_style_unref(query);
4570 sp_desktop_set_style (desktop, css, true, true);
4571 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4572 _("Text: Change alignment"));
4573 sp_repr_css_attr_unref (css);
4575 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4576 }
4578 void
4579 sp_text_toolbox_style_toggled (GtkToggleButton *button,
4580 gpointer data)
4581 {
4582 if (g_object_get_data (G_OBJECT (button), "block")) return;
4584 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4585 SPCSSAttr *css = sp_repr_css_attr_new ();
4586 int prop = GPOINTER_TO_INT(data);
4587 bool active = gtk_toggle_button_get_active (button);
4589 SPStyle *query =
4590 sp_style_new (SP_ACTIVE_DOCUMENT);
4592 int result_fontspec =
4593 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4595 int result_family =
4596 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4598 int result_style =
4599 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4601 int result_numbers =
4602 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4604 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4605 Glib::ustring newFontSpec = "";
4607 if (fontSpec.empty()) {
4608 // Construct a new font specification if it does not yet exist
4609 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4610 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4611 fontFromStyle->Unref();
4612 }
4614 switch (prop)
4615 {
4616 case 0:
4617 {
4618 if (!fontSpec.empty()) {
4619 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4620 }
4621 if (fontSpec != newFontSpec) {
4622 // Don't even set the bold if the font didn't exist on the system
4623 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4624 }
4625 break;
4626 }
4628 case 1:
4629 {
4630 if (!fontSpec.empty()) {
4631 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4632 }
4633 if (fontSpec != newFontSpec) {
4634 // Don't even set the italic if the font didn't exist on the system
4635 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4636 }
4637 break;
4638 }
4639 }
4641 if (!newFontSpec.empty()) {
4642 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4643 }
4645 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4646 if (result_fontspec == QUERY_STYLE_NOTHING)
4647 {
4648 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4649 }
4651 sp_style_unref(query);
4653 sp_desktop_set_style (desktop, css, true, true);
4654 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4655 _("Text: Change font style"));
4656 sp_repr_css_attr_unref (css);
4658 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4659 }
4661 void
4662 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
4663 gpointer data)
4664 {
4665 if (g_object_get_data (G_OBJECT (button), "block")) {
4666 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4667 return;
4668 }
4670 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4671 SPCSSAttr *css = sp_repr_css_attr_new ();
4672 int prop = GPOINTER_TO_INT(data);
4674 switch (prop)
4675 {
4676 case 0:
4677 {
4678 sp_repr_css_set_property (css, "writing-mode", "lr");
4679 break;
4680 }
4682 case 1:
4683 {
4684 sp_repr_css_set_property (css, "writing-mode", "tb");
4685 break;
4686 }
4687 }
4689 SPStyle *query =
4690 sp_style_new (SP_ACTIVE_DOCUMENT);
4691 int result_numbers =
4692 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4694 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4695 if (result_numbers == QUERY_STYLE_NOTHING)
4696 {
4697 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4698 }
4700 sp_desktop_set_style (desktop, css, true, true);
4701 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4702 _("Text: Change orientation"));
4703 sp_repr_css_attr_unref (css);
4705 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4706 }
4708 gboolean
4709 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4710 {
4711 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4712 if (!desktop) return FALSE;
4714 switch (get_group0_keyval (event)) {
4715 case GDK_Escape: // defocus
4716 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4717 sp_text_toolbox_selection_changed (NULL, tbl); // update
4718 return TRUE; // I consumed the event
4719 break;
4720 }
4721 return FALSE;
4722 }
4724 gboolean
4725 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4726 {
4727 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4728 if (!desktop) return FALSE;
4730 switch (get_group0_keyval (event)) {
4731 case GDK_KP_Enter:
4732 case GDK_Return:
4733 case GDK_Escape: // defocus
4734 gtk_widget_hide (w);
4735 popdown_visible = false;
4736 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4737 return TRUE; // I consumed the event
4738 break;
4739 case GDK_w:
4740 case GDK_W:
4741 if (event->state & GDK_CONTROL_MASK) {
4742 gtk_widget_hide (w);
4743 popdown_visible = false;
4744 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4745 return TRUE; // I consumed the event
4746 }
4747 break;
4748 }
4749 return FALSE;
4750 }
4753 void
4754 sp_text_toolbox_size_changed (GtkComboBox *cbox,
4755 GObject *tbl)
4756 {
4757 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4759 if (g_object_get_data (tbl, "size-block")) return;
4761 // If this is not from selecting a size in the list (in which case get_active will give the
4762 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4763 // process this event. This fixes GTK's stupid insistence on sending an activate change every
4764 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4765 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4766 return;
4768 gchar *endptr;
4769 gdouble value = -1;
4770 char *text = gtk_combo_box_get_active_text (cbox);
4771 if (text) {
4772 value = g_strtod (text, &endptr);
4773 if (endptr == text) // conversion failed, non-numeric input
4774 value = -1;
4775 free (text);
4776 }
4777 if (value <= 0) {
4778 return; // could not parse value
4779 }
4781 SPCSSAttr *css = sp_repr_css_attr_new ();
4782 Inkscape::CSSOStringStream osfs;
4783 osfs << value;
4784 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4786 SPStyle *query =
4787 sp_style_new (SP_ACTIVE_DOCUMENT);
4788 int result_numbers =
4789 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4791 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4792 if (result_numbers == QUERY_STYLE_NOTHING)
4793 {
4794 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4795 }
4797 sp_style_unref(query);
4799 sp_desktop_set_style (desktop, css, true, true);
4800 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4801 _("Text: Change font size"));
4802 sp_repr_css_attr_unref (css);
4804 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4805 }
4807 gboolean
4808 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
4809 {
4810 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4811 if (!desktop) return FALSE;
4813 if (!g_object_get_data (tbl, "esc-pressed")) {
4814 g_object_set_data (tbl, "enter-pressed", gpointer(1));
4815 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4816 sp_text_toolbox_size_changed (cbox, tbl);
4817 g_object_set_data (tbl, "enter-pressed", gpointer(0));
4818 }
4819 return FALSE; // I consumed the event
4820 }
4823 gboolean
4824 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4825 {
4826 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4827 if (!desktop) return FALSE;
4829 switch (get_group0_keyval (event)) {
4830 case GDK_Escape: // defocus
4831 g_object_set_data (tbl, "esc-pressed", gpointer(1));
4832 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4833 g_object_set_data (tbl, "esc-pressed", gpointer(0));
4834 return TRUE; // I consumed the event
4835 break;
4836 case GDK_Return: // defocus
4837 case GDK_KP_Enter:
4838 g_object_set_data (tbl, "enter-pressed", gpointer(1));
4839 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4840 sp_text_toolbox_size_changed (cbox, tbl);
4841 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4842 g_object_set_data (tbl, "enter-pressed", gpointer(0));
4843 return TRUE; // I consumed the event
4844 break;
4845 }
4846 return FALSE;
4847 }
4849 void
4850 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
4851 GObject *tbl)
4852 {
4853 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4854 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4855 int x, y;
4857 if (!popdown_visible)
4858 {
4859 gdk_window_get_origin (widget->window, &x, &y);
4860 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4861 gtk_widget_show_all (popdown);
4862 //sp_transientize (popdown);
4864 gdk_pointer_grab (widget->window, TRUE,
4865 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4866 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4867 GDK_POINTER_MOTION_MASK),
4868 NULL, NULL, GDK_CURRENT_TIME);
4870 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4872 popdown_visible = true;
4873 }
4874 else
4875 {
4876 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4877 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4878 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4879 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4880 gtk_widget_hide (popdown);
4881 popdown_visible = false;
4882 }
4883 }
4885 gboolean
4886 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
4887 GdkEventFocus */*event*/,
4888 GObject */*tbl*/)
4889 {
4890 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4891 return FALSE;
4892 }
4894 gboolean
4895 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
4896 GdkEventFocus */*event*/,
4897 GObject */*tbl*/)
4898 {
4899 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4901 if (popdown_hasfocus) {
4902 gtk_widget_hide (popdown);
4903 popdown_hasfocus = false;
4904 popdown_visible = false;
4905 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4906 return TRUE;
4907 }
4908 return FALSE;
4909 }
4911 gboolean
4912 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
4913 GdkEventFocus */*event*/,
4914 GObject */*tbl*/)
4915 {
4916 popdown_hasfocus = true;
4917 return TRUE;
4918 }
4921 void
4922 cell_data_func (GtkTreeViewColumn */*column*/,
4923 GtkCellRenderer *cell,
4924 GtkTreeModel *tree_model,
4925 GtkTreeIter *iter,
4926 gpointer /*data*/)
4927 {
4928 char *family,
4929 *family_escaped,
4930 *sample_escaped;
4932 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4934 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4936 family_escaped = g_markup_escape_text (family, -1);
4937 sample_escaped = g_markup_escape_text (sample, -1);
4939 std::stringstream markup;
4940 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4941 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4943 free (family);
4944 free (family_escaped);
4945 free (sample_escaped);
4946 }
4948 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4949 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4950 if (completion) {
4951 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4952 g_object_unref (completion);
4953 }
4954 }
4956 GtkWidget*
4957 sp_text_toolbox_new (SPDesktop *desktop)
4958 {
4959 GtkWidget *tbl = gtk_hbox_new (FALSE, 0);
4961 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4962 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4964 GtkTooltips *tt = gtk_tooltips_new();
4965 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4967 ////////////Family
4968 //Window
4969 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4970 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4972 //Entry
4973 GtkWidget *entry = gtk_entry_new ();
4974 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4975 GtkEntryCompletion *completion = gtk_entry_completion_new ();
4976 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4977 gtk_entry_completion_set_text_column (completion, 0);
4978 gtk_entry_completion_set_minimum_key_length (completion, 1);
4979 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4980 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4981 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4982 aux_toolbox_space (tbl, 1);
4983 gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4984 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4986 //Button
4987 GtkWidget *button = gtk_button_new ();
4988 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4989 gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4991 //Popdown
4992 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
4993 GtkWidget *treeview = gtk_tree_view_new ();
4995 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
4996 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
4997 gtk_tree_view_column_pack_start (column, cell, FALSE);
4998 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4999 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5000 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5002 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5003 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5004 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5006 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5008 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5009 gtk_container_add (GTK_CONTAINER (sw), treeview);
5011 gtk_container_add (GTK_CONTAINER (window), sw);
5012 gtk_widget_set_size_request (window, 300, 450);
5014 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5015 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5016 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5018 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5020 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5021 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5022 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5024 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5025 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5027 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5028 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5029 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5030 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5031 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5033 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
5034 aux_toolbox_space (tbl, 1);
5035 GtkWidget *box = gtk_event_box_new ();
5036 gtk_container_add (GTK_CONTAINER (box), image);
5037 gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
5038 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5039 GtkTooltips *tooltips = gtk_tooltips_new ();
5040 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5041 gtk_widget_hide (GTK_WIDGET (box));
5042 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5044 ////////////Size
5045 const char *sizes[] = {
5046 "4", "6", "8", "9", "10", "11", "12", "13", "14",
5047 "16", "18", "20", "22", "24", "28",
5048 "32", "36", "40", "48", "56", "64", "72", "144"
5049 };
5051 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5052 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5053 gtk_widget_set_size_request (cbox, 80, -1);
5054 aux_toolbox_space (tbl, 1);
5055 gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
5056 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5057 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5058 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5059 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5061 //spacer
5062 aux_toolbox_space (tbl, 4);
5063 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5065 ////////////Text anchor
5066 GtkWidget *group = gtk_radio_button_new (NULL);
5067 GtkWidget *row = gtk_hbox_new (FALSE, 4);
5068 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5070 // left
5071 GtkWidget *rbutton = group;
5072 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5073 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
5074 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5076 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5077 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
5078 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5079 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5081 // center
5082 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5083 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5084 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
5085 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5087 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5088 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
5089 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5090 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5092 // right
5093 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5094 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5095 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
5096 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5098 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5099 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5100 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5101 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5103 // fill
5104 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5105 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5106 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
5107 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5109 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5110 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
5111 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5112 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5114 aux_toolbox_space (tbl, 1);
5115 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5117 //spacer
5118 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5120 ////////////Text style
5121 row = gtk_hbox_new (FALSE, 4);
5123 // bold
5124 rbutton = gtk_toggle_button_new ();
5125 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5126 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
5127 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5128 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5130 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5131 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
5132 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5134 // italic
5135 rbutton = gtk_toggle_button_new ();
5136 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5137 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
5138 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5139 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5141 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5142 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
5143 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5145 aux_toolbox_space (tbl, 1);
5146 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5148 //spacer
5149 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5151 ////////////Text orientation
5152 group = gtk_radio_button_new (NULL);
5153 row = gtk_hbox_new (FALSE, 4);
5154 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5156 // horizontal
5157 rbutton = group;
5158 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5159 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
5160 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5161 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5163 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5164 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5165 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5167 // vertical
5168 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5169 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5170 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
5171 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5172 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5174 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5175 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
5176 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5177 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5180 //watch selection
5181 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5183 sigc::connection *c_selection_changed =
5184 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5185 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5186 pool->add_connection ("selection-changed", c_selection_changed);
5188 sigc::connection *c_selection_modified =
5189 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5190 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5191 pool->add_connection ("selection-modified", c_selection_modified);
5193 sigc::connection *c_subselection_changed =
5194 new sigc::connection (desktop->connectToolSubselectionChanged
5195 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5196 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5198 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5201 gtk_widget_show_all (tbl);
5202 return tbl;
5204 } // end of sp_text_toolbox_new()
5206 }//<unnamed> namespace
5209 //#########################
5210 //## Connector ##
5211 //#########################
5213 static void sp_connector_path_set_avoid(void)
5214 {
5215 cc_selection_set_avoid(true);
5216 }
5219 static void sp_connector_path_set_ignore(void)
5220 {
5221 cc_selection_set_avoid(false);
5222 }
5226 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5227 {
5228 // quit if run by the _changed callbacks
5229 if (g_object_get_data( tbl, "freeze" )) {
5230 return;
5231 }
5233 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5234 SPDocument *doc = sp_desktop_document(desktop);
5236 if (!sp_document_get_undo_sensitive(doc))
5237 {
5238 return;
5239 }
5241 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5243 if ( repr->attribute("inkscape:connector-spacing") ) {
5244 gdouble priorValue = gtk_adjustment_get_value(adj);
5245 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5246 if ( priorValue == gtk_adjustment_get_value(adj) ) {
5247 return;
5248 }
5249 } else if ( adj->value == defaultConnSpacing ) {
5250 return;
5251 }
5253 // in turn, prevent callbacks from responding
5254 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5256 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5257 SP_OBJECT(desktop->namedview)->updateRepr();
5259 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5260 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5261 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5262 NR::Matrix m = NR::identity();
5263 avoid_item_move(&m, item);
5264 }
5266 if (items) {
5267 g_slist_free(items);
5268 }
5270 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5271 _("Change connector spacing"));
5273 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5275 spinbutton_defocus(GTK_OBJECT(tbl));
5276 }
5278 static void sp_connector_graph_layout(void)
5279 {
5280 if (!SP_ACTIVE_DESKTOP) return;
5282 // hack for clones, see comment in align-and-distribute.cpp
5283 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5284 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5286 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5288 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5290 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5291 }
5293 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5294 {
5295 if ( gtk_toggle_action_get_active( act ) ) {
5296 prefs_set_string_attribute("tools.connector", "directedlayout",
5297 "true");
5298 } else {
5299 prefs_set_string_attribute("tools.connector", "directedlayout",
5300 "false");
5301 }
5302 }
5304 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5305 {
5306 if ( gtk_toggle_action_get_active( act ) ) {
5307 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5308 "true");
5309 } else {
5310 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5311 "false");
5312 }
5313 }
5316 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5317 {
5318 prefs_set_double_attribute("tools.connector", "length", adj->value);
5319 spinbutton_defocus(GTK_OBJECT(tbl));
5320 }
5322 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5323 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5324 bool /*is_interactive*/, gpointer data)
5325 {
5326 GtkWidget *tbl = GTK_WIDGET(data);
5328 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5329 return;
5330 }
5331 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5332 return;
5333 }
5335 GtkAdjustment *adj = (GtkAdjustment*)
5336 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5337 gdouble spacing = defaultConnSpacing;
5338 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5340 gtk_adjustment_set_value(adj, spacing);
5341 }
5344 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5345 NULL, /* child_added */
5346 NULL, /* child_removed */
5347 connector_tb_event_attr_changed,
5348 NULL, /* content_changed */
5349 NULL /* order_changed */
5350 };
5353 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5354 {
5355 {
5356 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5357 _("Avoid"),
5358 _("Make connectors avoid selected objects"),
5359 "connector_avoid",
5360 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5361 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5362 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5363 }
5365 {
5366 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5367 _("Ignore"),
5368 _("Make connectors ignore selected objects"),
5369 "connector_ignore",
5370 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5371 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5372 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5373 }
5375 EgeAdjustmentAction* eact = 0;
5377 // Spacing spinbox
5378 eact = create_adjustment_action( "ConnectorSpacingAction",
5379 _("Connector Spacing"), _("Spacing:"),
5380 _("The amount of space left around objects by auto-routing connectors"),
5381 "tools.connector", "spacing", defaultConnSpacing,
5382 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5383 0, 100, 1.0, 10.0,
5384 0, 0, 0,
5385 connector_spacing_changed, 1, 0 );
5386 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5388 // Graph (connector network) layout
5389 {
5390 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5391 _("Graph"),
5392 _("Nicely arrange selected connector network"),
5393 "graph_layout",
5394 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5395 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5396 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5397 }
5399 // Default connector length spinbox
5400 eact = create_adjustment_action( "ConnectorLengthAction",
5401 _("Connector Length"), _("Length:"),
5402 _("Ideal length for connectors when layout is applied"),
5403 "tools.connector", "length", 100,
5404 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5405 10, 1000, 10.0, 100.0,
5406 0, 0, 0,
5407 connector_length_changed, 1, 0 );
5408 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5411 // Directed edges toggle button
5412 {
5413 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5414 _("Downwards"),
5415 _("Make connectors with end-markers (arrows) point downwards"),
5416 "directed_graph",
5417 Inkscape::ICON_SIZE_DECORATION );
5418 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5420 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5421 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5422 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5424 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5425 }
5427 // Avoid overlaps toggle button
5428 {
5429 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5430 _("Remove overlaps"),
5431 _("Do not allow overlapping shapes"),
5432 "remove_overlaps",
5433 Inkscape::ICON_SIZE_DECORATION );
5434 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5436 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5437 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5438 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5440 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5441 }
5443 // Code to watch for changes to the connector-spacing attribute in
5444 // the XML.
5445 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5446 g_assert(repr != NULL);
5448 purge_repr_listener( holder, holder );
5450 if (repr) {
5451 g_object_set_data( holder, "repr", repr );
5452 Inkscape::GC::anchor(repr);
5453 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5454 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5455 }
5456 } // end of sp_connector_toolbox_prep()
5459 //#########################
5460 //## Paintbucket ##
5461 //#########################
5463 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5464 {
5465 gint channels = ege_select_one_action_get_active( act );
5466 flood_channels_set_channels( channels );
5467 }
5469 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5470 {
5471 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5472 }
5474 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5475 {
5476 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5477 }
5479 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5480 {
5481 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5482 SPUnit const *unit = tracker->getActiveUnit();
5484 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5486 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5487 }
5489 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5490 {
5491 // FIXME: make defaults settable via Inkscape Options
5492 struct KeyValue {
5493 char const *key;
5494 double value;
5495 } const key_values[] = {
5496 {"threshold", 15},
5497 {"offset", 0.0}
5498 };
5500 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5501 KeyValue const &kv = key_values[i];
5502 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5503 if ( adj ) {
5504 gtk_adjustment_set_value(adj, kv.value);
5505 }
5506 }
5508 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5509 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5510 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5511 ege_select_one_action_set_active( autogap_action, 0 );
5512 }
5514 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5515 {
5516 EgeAdjustmentAction* eact = 0;
5518 {
5519 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5521 GList* items = 0;
5522 gint count = 0;
5523 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5524 {
5525 GtkTreeIter iter;
5526 gtk_list_store_append( model, &iter );
5527 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5528 count++;
5529 }
5530 g_list_free( items );
5531 items = 0;
5532 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5533 g_object_set( act1, "short_label", _("Fill by:"), NULL );
5534 ege_select_one_action_set_appearance( act1, "compact" );
5535 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5536 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5537 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5538 g_object_set_data( holder, "channels_action", act1 );
5539 }
5541 // Spacing spinbox
5542 {
5543 eact = create_adjustment_action(
5544 "ThresholdAction",
5545 _("Fill Threshold"), _("Threshold:"),
5546 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5547 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5548 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5549 0, 0, 0,
5550 paintbucket_threshold_changed, 1, 0 );
5552 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5553 }
5555 // Create the units menu.
5556 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5557 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5558 if (stored_unit)
5559 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5560 g_object_set_data( holder, "tracker", tracker );
5561 {
5562 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5563 gtk_action_group_add_action( mainActions, act );
5564 }
5566 // Offset spinbox
5567 {
5568 eact = create_adjustment_action(
5569 "OffsetAction",
5570 _("Grow/shrink by"), _("Grow/shrink by:"),
5571 _("The amount to grow (positive) or shrink (negative) the created fill path"),
5572 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5573 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5574 0, 0, 0,
5575 paintbucket_offset_changed, 1, 2);
5576 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5578 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5579 }
5581 /* Auto Gap */
5582 {
5583 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5585 GList* items = 0;
5586 gint count = 0;
5587 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5588 {
5589 GtkTreeIter iter;
5590 gtk_list_store_append( model, &iter );
5591 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5592 count++;
5593 }
5594 g_list_free( items );
5595 items = 0;
5596 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5597 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5598 ege_select_one_action_set_appearance( act2, "compact" );
5599 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5600 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5601 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5602 g_object_set_data( holder, "autogap_action", act2 );
5603 }
5605 /* Reset */
5606 {
5607 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5608 _("Defaults"),
5609 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5610 GTK_STOCK_CLEAR );
5611 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5612 gtk_action_group_add_action( mainActions, act );
5613 gtk_action_set_sensitive( act, TRUE );
5614 }
5616 }
5618 /*
5619 Local Variables:
5620 mode:c++
5621 c-file-style:"stroustrup"
5622 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5623 indent-tabs-mode:nil
5624 fill-column:99
5625 End:
5626 */
5627 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :