1 /** \file
2 * Controls bars for some of Inkscape's tools
3 * (for some tools, they are in their own files)
4 */
6 /*
7 *
8 * Authors:
9 * MenTaLguY <mental@rydia.net>
10 * Lauris Kaplinski <lauris@kaplinski.com>
11 * bulia byak <buliabyak@users.sf.net>
12 * Frank Felfe <innerspace@iname.com>
13 * John Cliff <simarilius@yahoo.com>
14 * David Turner <novalis@gnu.org>
15 * Josh Andler <scislac@scislac.com>
16 * Jon A. Cruz <jon@joncruz.org>
17 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2006 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <cstring>
31 #include <string>
33 #include <gtkmm.h>
34 #include <gtk/gtk.h>
35 #include <iostream>
36 #include <sstream>
38 #include "widgets/button.h"
39 #include "widgets/widget-sizes.h"
40 #include "widgets/spw-utilities.h"
41 #include "widgets/spinbutton-events.h"
42 #include "dialogs/text-edit.h"
43 #include "dialogs/dialog-events.h"
45 #include "ui/widget/style-swatch.h"
47 #include "prefs-utils.h"
48 #include "verbs.h"
49 #include "sp-namedview.h"
50 #include "desktop.h"
51 #include "desktop-handles.h"
52 #include "xml/repr.h"
53 #include "xml/node-event-vector.h"
54 #include <glibmm/i18n.h>
55 #include "helper/unit-menu.h"
56 #include "helper/units.h"
57 #include "live_effects/effect.h"
59 #include "inkscape.h"
60 #include "conn-avoid-ref.h"
63 #include "select-toolbar.h"
64 #include "gradient-toolbar.h"
66 #include "connector-context.h"
67 #include "node-context.h"
68 #include "draw-context.h"
69 #include "shape-editor.h"
70 #include "tweak-context.h"
71 #include "sp-rect.h"
72 #include "box3d.h"
73 #include "box3d-context.h"
74 #include "sp-star.h"
75 #include "sp-spiral.h"
76 #include "sp-ellipse.h"
77 #include "sp-text.h"
78 #include "sp-flowtext.h"
79 #include "sp-clippath.h"
80 #include "sp-mask.h"
81 #include "style.h"
82 #include "selection.h"
83 #include "selection-chemistry.h"
84 #include "document-private.h"
85 #include "desktop-style.h"
86 #include "../libnrtype/font-lister.h"
87 #include "../libnrtype/font-instance.h"
88 #include "../connection-pool.h"
89 #include "../prefs-utils.h"
90 #include "../inkscape-stock.h"
91 #include "icon.h"
92 #include "graphlayout/graphlayout.h"
94 #include "mod360.h"
96 #include "toolbox.h"
98 #include "flood-context.h"
100 #include "ink-action.h"
101 #include "ege-adjustment-action.h"
102 #include "ege-output-action.h"
103 #include "ege-select-one-action.h"
104 #include "helper/unit-tracker.h"
106 #include "svg/css-ostringstream.h"
108 #include "widgets/calligraphic-profile-rename.h"
110 using Inkscape::UnitTracker;
112 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
113 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
115 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
128 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
135 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
136 static Inkscape::IconSize sizeChoices[] = {
137 Inkscape::ICON_SIZE_LARGE_TOOLBAR,
138 Inkscape::ICON_SIZE_SMALL_TOOLBAR,
139 Inkscape::ICON_SIZE_MENU
140 };
141 int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
142 return sizeChoices[index];
143 }
145 static struct {
146 gchar const *type_name;
147 gchar const *data_name;
148 sp_verb_t verb;
149 sp_verb_t doubleclick_verb;
150 } const tools[] = {
151 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
152 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
153 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
154 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
155 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
156 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
157 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
158 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
159 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
160 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
161 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
162 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
163 { "SPEraserContext", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
164 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
165 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
166 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
167 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
168 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
169 { NULL, NULL, 0, 0 }
170 };
172 static struct {
173 gchar const *type_name;
174 gchar const *data_name;
175 GtkWidget *(*create_func)(SPDesktop *desktop);
176 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
177 gchar const *ui_name;
178 gint swatch_verb_id;
179 gchar const *swatch_tool;
180 gchar const *swatch_tip;
181 } const aux_toolboxes[] = {
182 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
183 SP_VERB_INVALID, 0, 0},
184 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
185 SP_VERB_INVALID, 0, 0},
186 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
187 SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
188 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
189 SP_VERB_INVALID, 0, 0},
190 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
191 SP_VERB_CONTEXT_STAR_PREFS, "tools.shapes.star", N_("Style of new stars")},
192 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
193 SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", N_("Style of new rectangles")},
194 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
195 SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", N_("Style of new 3D boxes")},
196 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
197 SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", N_("Style of new ellipses")},
198 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
199 SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral", N_("Style of new spirals")},
200 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
201 SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
202 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
203 SP_VERB_CONTEXT_PEN_PREFS, "tools.freehand.pen", N_("Style of new paths created by Pen")},
204 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
205 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
206 { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
207 SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
208 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
209 SP_VERB_INVALID, 0, 0},
210 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
211 SP_VERB_INVALID, 0, 0},
212 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
213 SP_VERB_INVALID, 0, 0},
214 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
215 SP_VERB_INVALID, 0, 0},
216 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
217 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
218 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
219 };
222 static gchar const * ui_descr =
223 "<ui>"
224 " <toolbar name='SelectToolbar'>"
225 " <toolitem action='EditSelectAll' />"
226 " <toolitem action='EditSelectAllInAllLayers' />"
227 " <toolitem action='EditDeselect' />"
228 " <separator />"
229 " <toolitem action='ObjectRotate90CCW' />"
230 " <toolitem action='ObjectRotate90' />"
231 " <toolitem action='ObjectFlipHorizontally' />"
232 " <toolitem action='ObjectFlipVertically' />"
233 " <separator />"
234 " <toolitem action='SelectionToBack' />"
235 " <toolitem action='SelectionLower' />"
236 " <toolitem action='SelectionRaise' />"
237 " <toolitem action='SelectionToFront' />"
238 " <separator />"
239 " <toolitem action='XAction' />"
240 " <toolitem action='YAction' />"
241 " <toolitem action='WidthAction' />"
242 " <toolitem action='LockAction' />"
243 " <toolitem action='HeightAction' />"
244 " <toolitem action='UnitsAction' />"
245 " <separator />"
246 " <toolitem action='transform_affect_label' />"
247 " <toolitem action='transform_stroke' />"
248 " <toolitem action='transform_corners' />"
249 " <toolitem action='transform_gradient' />"
250 " <toolitem action='transform_pattern' />"
251 " </toolbar>"
253 " <toolbar name='NodeToolbar'>"
254 " <toolitem action='NodeInsertAction' />"
255 " <toolitem action='NodeDeleteAction' />"
256 " <separator />"
257 " <toolitem action='NodeJoinAction' />"
258 " <toolitem action='NodeBreakAction' />"
259 " <separator />"
260 " <toolitem action='NodeJoinSegmentAction' />"
261 " <toolitem action='NodeDeleteSegmentAction' />"
262 " <separator />"
263 " <toolitem action='NodeCuspAction' />"
264 " <toolitem action='NodeSmoothAction' />"
265 " <toolitem action='NodeSymmetricAction' />"
266 " <separator />"
267 " <toolitem action='NodeLineAction' />"
268 " <toolitem action='NodeCurveAction' />"
269 " <separator />"
270 " <toolitem action='ObjectToPath' />"
271 " <toolitem action='StrokeToPath' />"
272 " <separator />"
273 " <toolitem action='NodeXAction' />"
274 " <toolitem action='NodeYAction' />"
275 " <toolitem action='NodeUnitsAction' />"
276 " <separator />"
277 " <toolitem action='ObjectEditClipPathAction' />"
278 " <toolitem action='ObjectEditMaskPathAction' />"
279 " <toolitem action='EditNextLPEParameterAction' />"
280 " <separator />"
281 " <toolitem action='NodesShowHandlesAction' />"
282 " <toolitem action='NodesShowHelperpath' />"
283 " </toolbar>"
285 " <toolbar name='TweakToolbar'>"
286 " <toolitem action='TweakWidthAction' />"
287 " <separator />"
288 " <toolitem action='TweakForceAction' />"
289 " <toolitem action='TweakPressureAction' />"
290 " <separator />"
291 " <toolitem action='TweakModeAction' />"
292 " <separator />"
293 " <toolitem action='TweakFidelityAction' />"
294 " <separator />"
295 " <toolitem action='TweakChannelsLabel' />"
296 " <toolitem action='TweakDoH' />"
297 " <toolitem action='TweakDoS' />"
298 " <toolitem action='TweakDoL' />"
299 " <toolitem action='TweakDoO' />"
300 " </toolbar>"
302 " <toolbar name='ZoomToolbar'>"
303 " <toolitem action='ZoomIn' />"
304 " <toolitem action='ZoomOut' />"
305 " <separator />"
306 " <toolitem action='Zoom1:0' />"
307 " <toolitem action='Zoom1:2' />"
308 " <toolitem action='Zoom2:1' />"
309 " <separator />"
310 " <toolitem action='ZoomSelection' />"
311 " <toolitem action='ZoomDrawing' />"
312 " <toolitem action='ZoomPage' />"
313 " <toolitem action='ZoomPageWidth' />"
314 " <separator />"
315 " <toolitem action='ZoomPrev' />"
316 " <toolitem action='ZoomNext' />"
317 " </toolbar>"
319 " <toolbar name='StarToolbar'>"
320 " <separator />"
321 " <toolitem action='StarStateAction' />"
322 " <separator />"
323 " <toolitem action='FlatAction' />"
324 " <separator />"
325 " <toolitem action='MagnitudeAction' />"
326 " <toolitem action='SpokeAction' />"
327 " <toolitem action='RoundednessAction' />"
328 " <toolitem action='RandomizationAction' />"
329 " <separator />"
330 " <toolitem action='StarResetAction' />"
331 " </toolbar>"
333 " <toolbar name='RectToolbar'>"
334 " <toolitem action='RectStateAction' />"
335 " <toolitem action='RectWidthAction' />"
336 " <toolitem action='RectHeightAction' />"
337 " <toolitem action='RadiusXAction' />"
338 " <toolitem action='RadiusYAction' />"
339 " <toolitem action='RectUnitsAction' />"
340 " <separator />"
341 " <toolitem action='RectResetAction' />"
342 " </toolbar>"
344 " <toolbar name='3DBoxToolbar'>"
345 " <toolitem action='3DBoxAngleXAction' />"
346 " <toolitem action='3DBoxVPXStateAction' />"
347 " <separator />"
348 " <toolitem action='3DBoxAngleYAction' />"
349 " <toolitem action='3DBoxVPYStateAction' />"
350 " <separator />"
351 " <toolitem action='3DBoxAngleZAction' />"
352 " <toolitem action='3DBoxVPZStateAction' />"
353 " </toolbar>"
355 " <toolbar name='SpiralToolbar'>"
356 " <toolitem action='SpiralStateAction' />"
357 " <toolitem action='SpiralRevolutionAction' />"
358 " <toolitem action='SpiralExpansionAction' />"
359 " <toolitem action='SpiralT0Action' />"
360 " <separator />"
361 " <toolitem action='SpiralResetAction' />"
362 " </toolbar>"
364 " <toolbar name='PenToolbar'>"
365 " <toolitem action='SpiroSplineModeActionPen' />"
366 " </toolbar>"
368 " <toolbar name='PencilToolbar'>"
369 " <toolitem action='SpiroSplineModeActionPencil' />"
370 " </toolbar>"
372 " <toolbar name='CalligraphyToolbar'>"
373 " <separator />"
374 " <toolitem action='SetProfileAction'/>"
375 " <toolitem action='SaveDeleteProfileAction'/>"
376 " <separator />"
377 " <toolitem action='CalligraphyWidthAction' />"
378 " <toolitem action='PressureAction' />"
379 " <toolitem action='TraceAction' />"
380 " <toolitem action='ThinningAction' />"
381 " <separator />"
382 " <toolitem action='AngleAction' />"
383 " <toolitem action='TiltAction' />"
384 " <toolitem action='FixationAction' />"
385 " <separator />"
386 " <toolitem action='CapRoundingAction' />"
387 " <separator />"
388 " <toolitem action='TremorAction' />"
389 " <toolitem action='WiggleAction' />"
390 " <toolitem action='MassAction' />"
391 " <separator />"
392 " </toolbar>"
394 " <toolbar name='ArcToolbar'>"
395 " <toolitem action='ArcStateAction' />"
396 " <separator />"
397 " <toolitem action='ArcStartAction' />"
398 " <toolitem action='ArcEndAction' />"
399 " <separator />"
400 " <toolitem action='ArcOpenAction' />"
401 " <separator />"
402 " <toolitem action='ArcResetAction' />"
403 " <separator />"
404 " </toolbar>"
406 " <toolbar name='PaintbucketToolbar'>"
407 " <toolitem action='ChannelsAction' />"
408 " <separator />"
409 " <toolitem action='ThresholdAction' />"
410 " <separator />"
411 " <toolitem action='OffsetAction' />"
412 " <toolitem action='PaintbucketUnitsAction' />"
413 " <separator />"
414 " <toolitem action='AutoGapAction' />"
415 " <separator />"
416 " <toolitem action='PaintbucketResetAction' />"
417 " </toolbar>"
419 " <toolbar name='EraserToolbar'>"
420 " <toolitem action='EraserWidthAction' />"
421 " <separator />"
422 " <toolitem action='EraserModeAction' />"
423 " </toolbar>"
425 " <toolbar name='DropperToolbar'>"
426 " <toolitem action='DropperOpacityAction' />"
427 " <toolitem action='DropperPickAlphaAction' />"
428 " <toolitem action='DropperSetAlphaAction' />"
429 " </toolbar>"
431 " <toolbar name='ConnectorToolbar'>"
432 " <toolitem action='ConnectorAvoidAction' />"
433 " <toolitem action='ConnectorIgnoreAction' />"
434 " <toolitem action='ConnectorSpacingAction' />"
435 " <toolitem action='ConnectorGraphAction' />"
436 " <toolitem action='ConnectorLengthAction' />"
437 " <toolitem action='ConnectorDirectedAction' />"
438 " <toolitem action='ConnectorOverlapAction' />"
439 " </toolbar>"
441 "</ui>"
442 ;
444 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
446 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
448 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
449 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
451 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
452 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
454 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
455 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
457 /* Global text entry widgets necessary for update */
458 /* GtkWidget *dropper_rgb_entry,
459 *dropper_opacity_entry ; */
460 // should be made a private member once this is converted to class
462 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
463 connection->disconnect();
464 delete connection;
465 }
467 static void purge_repr_listener( GObject* obj, GObject* tbl )
468 {
469 (void)obj;
470 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
471 if (oldrepr) { // remove old listener
472 sp_repr_remove_listener_by_data(oldrepr, tbl);
473 Inkscape::GC::release(oldrepr);
474 oldrepr = 0;
475 g_object_set_data( tbl, "repr", NULL );
476 }
477 }
479 GtkWidget *
480 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
481 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
482 Inkscape::UI::View::View *view, GtkTooltips *tt)
483 {
484 SPAction *action = verb->get_action(view);
485 if (!action) return NULL;
487 SPAction *doubleclick_action;
488 if (doubleclick_verb)
489 doubleclick_action = doubleclick_verb->get_action(view);
490 else
491 doubleclick_action = NULL;
493 /* fixme: Handle sensitive/unsensitive */
494 /* fixme: Implement sp_button_new_from_action */
495 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
496 gtk_widget_show(b);
497 gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
499 return b;
500 }
502 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
503 Inkscape::UI::View::View *view, GtkTooltips *tt)
504 {
505 return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
506 }
508 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
509 Inkscape::UI::View::View *view, GtkTooltips *tt)
510 {
511 return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
512 }
515 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
516 {
517 SPAction* targetAction = SP_ACTION(user_data);
518 if ( targetAction ) {
519 sp_action_perform( targetAction, NULL );
520 }
521 }
523 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
524 {
525 if ( data ) {
526 GtkAction* act = GTK_ACTION(data);
527 gtk_action_set_sensitive( act, sensitive );
528 }
529 }
531 static SPActionEventVector action_event_vector = {
532 {NULL},
533 NULL,
534 NULL,
535 sp_action_action_set_sensitive,
536 NULL,
537 NULL
538 };
540 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
541 {
542 GtkAction* act = 0;
544 SPAction* targetAction = verb->get_action(view);
545 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
546 act = GTK_ACTION(inky);
547 gtk_action_set_sensitive( act, targetAction->sensitive );
549 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
551 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
552 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
554 return act;
555 }
557 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
558 {
559 Inkscape::UI::View::View *view = desktop;
560 gint verbsToUse[] = {
561 // disabled until we have icons for them:
562 //find
563 //SP_VERB_EDIT_TILE,
564 //SP_VERB_EDIT_UNTILE,
565 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
566 SP_VERB_DIALOG_DISPLAY,
567 SP_VERB_DIALOG_FILL_STROKE,
568 SP_VERB_DIALOG_NAMEDVIEW,
569 SP_VERB_DIALOG_TEXT,
570 SP_VERB_DIALOG_XML_EDITOR,
571 SP_VERB_EDIT_CLONE,
572 SP_VERB_EDIT_COPY,
573 SP_VERB_EDIT_CUT,
574 SP_VERB_EDIT_DUPLICATE,
575 SP_VERB_EDIT_PASTE,
576 SP_VERB_EDIT_REDO,
577 SP_VERB_EDIT_UNDO,
578 SP_VERB_EDIT_UNLINK_CLONE,
579 SP_VERB_FILE_EXPORT,
580 SP_VERB_FILE_IMPORT,
581 SP_VERB_FILE_NEW,
582 SP_VERB_FILE_OPEN,
583 SP_VERB_FILE_PRINT,
584 SP_VERB_FILE_SAVE,
585 SP_VERB_OBJECT_TO_CURVE,
586 SP_VERB_SELECTION_GROUP,
587 SP_VERB_SELECTION_OUTLINE,
588 SP_VERB_SELECTION_UNGROUP,
589 SP_VERB_ZOOM_1_1,
590 SP_VERB_ZOOM_1_2,
591 SP_VERB_ZOOM_2_1,
592 SP_VERB_ZOOM_DRAWING,
593 SP_VERB_ZOOM_IN,
594 SP_VERB_ZOOM_NEXT,
595 SP_VERB_ZOOM_OUT,
596 SP_VERB_ZOOM_PAGE,
597 SP_VERB_ZOOM_PAGE_WIDTH,
598 SP_VERB_ZOOM_PREV,
599 SP_VERB_ZOOM_SELECTION,
600 };
602 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
604 static std::map<SPDesktop*, GtkActionGroup*> groups;
605 GtkActionGroup* mainActions = 0;
606 if ( groups.find(desktop) != groups.end() ) {
607 mainActions = groups[desktop];
608 }
610 if ( !mainActions ) {
611 mainActions = gtk_action_group_new("main");
612 groups[desktop] = mainActions;
613 }
615 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
616 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
617 if ( verb ) {
618 if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
619 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
620 gtk_action_group_add_action( mainActions, act );
621 }
622 }
623 }
625 return mainActions;
626 }
629 GtkWidget *
630 sp_tool_toolbox_new()
631 {
632 GtkTooltips *tt = gtk_tooltips_new();
633 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
635 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
636 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
638 gtk_widget_set_sensitive(tb, FALSE);
640 GtkWidget *hb = gtk_handle_box_new();
641 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
642 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
643 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
645 gtk_container_add(GTK_CONTAINER(hb), tb);
646 gtk_widget_show(GTK_WIDGET(tb));
648 sigc::connection* conn = new sigc::connection;
649 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
651 return hb;
652 }
654 static void
655 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
656 {
657 g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
658 gtk_widget_queue_resize(child);
659 }
661 static void
662 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
663 {
664 g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
665 gtk_widget_queue_resize(child);
666 }
668 GtkWidget *
669 sp_aux_toolbox_new()
670 {
671 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
673 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
675 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
677 gtk_widget_set_sensitive(tb, FALSE);
679 GtkWidget *hb = gtk_handle_box_new();
680 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
681 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
682 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
684 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
685 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
687 gtk_container_add(GTK_CONTAINER(hb), tb);
688 gtk_widget_show(GTK_WIDGET(tb));
690 sigc::connection* conn = new sigc::connection;
691 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
693 return hb;
694 }
696 //####################################
697 //# Commands Bar
698 //####################################
700 GtkWidget *
701 sp_commands_toolbox_new()
702 {
703 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
705 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
707 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
708 gtk_widget_set_sensitive(tb, FALSE);
710 GtkWidget *hb = gtk_handle_box_new();
711 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
712 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
713 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
715 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
716 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
718 gtk_container_add(GTK_CONTAINER(hb), tb);
719 gtk_widget_show(GTK_WIDGET(tb));
721 sigc::connection* conn = new sigc::connection;
722 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
724 return hb;
725 }
727 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
728 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
729 gchar const *path, gchar const *data, gdouble def,
730 GtkWidget *focusTarget,
731 GtkWidget *us,
732 GObject *dataKludge,
733 gboolean altx, gchar const *altx_mark,
734 gdouble lower, gdouble upper, gdouble step, gdouble page,
735 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
736 void (*callback)(GtkAdjustment *, GObject *),
737 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
738 {
739 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
740 lower, upper, step, page, page ) );
741 if (us) {
742 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
743 }
745 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
747 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
748 if ( shortLabel ) {
749 g_object_set( act, "short_label", shortLabel, NULL );
750 }
752 if ( (descrCount > 0) && descrLabels && descrValues ) {
753 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
754 }
756 if ( focusTarget ) {
757 ege_adjustment_action_set_focuswidget( act, focusTarget );
758 }
760 if ( altx && altx_mark ) {
761 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
762 }
764 if ( dataKludge ) {
765 g_object_set_data( dataKludge, data, adj );
766 }
768 // Using a cast just to make sure we pass in the right kind of function pointer
769 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
771 return act;
772 }
775 //####################################
776 //# node editing callbacks
777 //####################################
779 /**
780 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
781 */
782 static ShapeEditor *get_current_shape_editor()
783 {
784 if (!SP_ACTIVE_DESKTOP) {
785 return NULL;
786 }
788 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
790 if (!SP_IS_NODE_CONTEXT(event_context)) {
791 return NULL;
792 }
794 return SP_NODE_CONTEXT(event_context)->shape_editor;
795 }
798 void
799 sp_node_path_edit_add(void)
800 {
801 ShapeEditor *shape_editor = get_current_shape_editor();
802 if (shape_editor) shape_editor->add_node();
803 }
805 void
806 sp_node_path_edit_delete(void)
807 {
808 ShapeEditor *shape_editor = get_current_shape_editor();
809 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
810 }
812 void
813 sp_node_path_edit_delete_segment(void)
814 {
815 ShapeEditor *shape_editor = get_current_shape_editor();
816 if (shape_editor) shape_editor->delete_segment();
817 }
819 void
820 sp_node_path_edit_break(void)
821 {
822 ShapeEditor *shape_editor = get_current_shape_editor();
823 if (shape_editor) shape_editor->break_at_nodes();
824 }
826 void
827 sp_node_path_edit_join(void)
828 {
829 ShapeEditor *shape_editor = get_current_shape_editor();
830 if (shape_editor) shape_editor->join_nodes();
831 }
833 void
834 sp_node_path_edit_join_segment(void)
835 {
836 ShapeEditor *shape_editor = get_current_shape_editor();
837 if (shape_editor) shape_editor->join_segments();
838 }
840 void
841 sp_node_path_edit_toline(void)
842 {
843 ShapeEditor *shape_editor = get_current_shape_editor();
844 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
845 }
847 void
848 sp_node_path_edit_tocurve(void)
849 {
850 ShapeEditor *shape_editor = get_current_shape_editor();
851 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
852 }
854 void
855 sp_node_path_edit_cusp(void)
856 {
857 ShapeEditor *shape_editor = get_current_shape_editor();
858 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
859 }
861 void
862 sp_node_path_edit_smooth(void)
863 {
864 ShapeEditor *shape_editor = get_current_shape_editor();
865 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
866 }
868 void
869 sp_node_path_edit_symmetrical(void)
870 {
871 ShapeEditor *shape_editor = get_current_shape_editor();
872 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
873 }
875 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
876 bool show = gtk_toggle_action_get_active( act );
877 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
878 ShapeEditor *shape_editor = get_current_shape_editor();
879 if (shape_editor) shape_editor->show_handles(show);
880 }
882 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
883 bool show = gtk_toggle_action_get_active( act );
884 prefs_set_int_attribute ("tools.nodes", "show_helperpath", show ? 1 : 0);
885 ShapeEditor *shape_editor = get_current_shape_editor();
886 if (shape_editor) shape_editor->show_helperpath(show);
887 }
889 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
890 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
891 }
893 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
894 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
895 }
897 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
898 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
899 }
901 /* is called when the node selection is modified */
902 static void
903 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
904 {
905 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
906 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
907 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
908 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
910 // quit if run by the attr_changed listener
911 if (g_object_get_data( tbl, "freeze" )) {
912 return;
913 }
915 // in turn, prevent listener from responding
916 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
918 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
919 SPUnit const *unit = tracker->getActiveUnit();
921 ShapeEditor *shape_editor = get_current_shape_editor();
922 if (shape_editor && shape_editor->has_nodepath()) {
923 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
924 int n_selected = 0;
925 if (nodepath) {
926 n_selected = nodepath->numSelected();
927 }
929 if (n_selected == 0) {
930 gtk_action_set_sensitive(xact, FALSE);
931 gtk_action_set_sensitive(yact, FALSE);
932 } else {
933 gtk_action_set_sensitive(xact, TRUE);
934 gtk_action_set_sensitive(yact, TRUE);
935 NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
936 NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
938 if (n_selected == 1) {
939 NR::Point sel_node = nodepath->singleSelectedCoords();
940 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
941 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
942 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
943 }
944 } else {
945 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
946 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
947 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
948 /* Note: Currently x and y will always have a value, even if the coordinates of the
949 selected nodes don't coincide (in this case we use the coordinates of the center
950 of the bounding box). So the entries are never set to zero. */
951 // FIXME: Maybe we should clear the entry if several nodes are selected
952 // instead of providing a kind of average value
953 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
954 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
955 }
956 }
957 }
958 } else {
959 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
960 gtk_action_set_sensitive(xact, FALSE);
961 gtk_action_set_sensitive(yact, FALSE);
962 }
964 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
965 }
967 static void
968 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
969 {
970 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
972 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
973 SPUnit const *unit = tracker->getActiveUnit();
975 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
976 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
977 }
979 // quit if run by the attr_changed listener
980 if (g_object_get_data( tbl, "freeze" )) {
981 return;
982 }
984 // in turn, prevent listener from responding
985 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
987 ShapeEditor *shape_editor = get_current_shape_editor();
988 if (shape_editor && shape_editor->has_nodepath()) {
989 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
990 if (!strcmp(value_name, "x")) {
991 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
992 }
993 if (!strcmp(value_name, "y")) {
994 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
995 }
996 }
998 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
999 }
1001 static void
1002 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1003 {
1004 sp_node_path_value_changed(adj, tbl, "x");
1005 }
1007 static void
1008 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1009 {
1010 sp_node_path_value_changed(adj, tbl, "y");
1011 }
1013 void
1014 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1015 {
1016 {
1017 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1018 SPItem *item = selection->singleItem();
1019 if (item && SP_IS_LPE_ITEM(item)) {
1020 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1021 gtk_action_set_sensitive(w, TRUE);
1022 } else {
1023 gtk_action_set_sensitive(w, FALSE);
1024 }
1025 } else {
1026 gtk_action_set_sensitive(w, FALSE);
1027 }
1028 }
1030 {
1031 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1032 SPItem *item = selection->singleItem();
1033 if (item && item->clip_ref && item->clip_ref->getObject()) {
1034 gtk_action_set_sensitive(w, TRUE);
1035 } else {
1036 gtk_action_set_sensitive(w, FALSE);
1037 }
1038 }
1040 {
1041 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1042 SPItem *item = selection->singleItem();
1043 if (item && item->mask_ref && item->mask_ref->getObject()) {
1044 gtk_action_set_sensitive(w, TRUE);
1045 } else {
1046 gtk_action_set_sensitive(w, FALSE);
1047 }
1048 }
1049 }
1051 void
1052 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1053 {
1054 sp_node_toolbox_sel_changed (selection, tbl);
1055 }
1059 //################################
1060 //## Node Editing Toolbox ##
1061 //################################
1063 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1064 {
1065 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1066 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1067 g_object_set_data( holder, "tracker", tracker );
1069 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1071 {
1072 InkAction* inky = ink_action_new( "NodeInsertAction",
1073 _("Insert node"),
1074 _("Insert new nodes into selected segments"),
1075 "node_insert",
1076 secondarySize );
1077 g_object_set( inky, "short_label", _("Insert"), NULL );
1078 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1079 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1080 }
1082 {
1083 InkAction* inky = ink_action_new( "NodeDeleteAction",
1084 _("Delete node"),
1085 _("Delete selected nodes"),
1086 "node_delete",
1087 secondarySize );
1088 g_object_set( inky, "short_label", _("Delete"), NULL );
1089 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1090 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1091 }
1093 {
1094 InkAction* inky = ink_action_new( "NodeJoinAction",
1095 _("Join endnodes"),
1096 _("Join selected endnodes"),
1097 "node_join",
1098 secondarySize );
1099 g_object_set( inky, "short_label", _("Join"), NULL );
1100 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1101 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1102 }
1104 {
1105 InkAction* inky = ink_action_new( "NodeBreakAction",
1106 _("Break nodes"),
1107 _("Break path at selected nodes"),
1108 "node_break",
1109 secondarySize );
1110 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1111 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1112 }
1115 {
1116 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1117 _("Join with segment"),
1118 _("Join selected endnodes with a new segment"),
1119 "node_join_segment",
1120 secondarySize );
1121 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1122 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1123 }
1125 {
1126 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1127 _("Delete segment"),
1128 _("Delete segment between two non-endpoint nodes"),
1129 "node_delete_segment",
1130 secondarySize );
1131 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1132 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1133 }
1135 {
1136 InkAction* inky = ink_action_new( "NodeCuspAction",
1137 _("Node Cusp"),
1138 _("Make selected nodes corner"),
1139 "node_cusp",
1140 secondarySize );
1141 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1142 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1143 }
1145 {
1146 InkAction* inky = ink_action_new( "NodeSmoothAction",
1147 _("Node Smooth"),
1148 _("Make selected nodes smooth"),
1149 "node_smooth",
1150 secondarySize );
1151 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1152 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1153 }
1155 {
1156 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1157 _("Node Symmetric"),
1158 _("Make selected nodes symmetric"),
1159 "node_symmetric",
1160 secondarySize );
1161 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1162 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1163 }
1165 {
1166 InkAction* inky = ink_action_new( "NodeLineAction",
1167 _("Node Line"),
1168 _("Make selected segments lines"),
1169 "node_line",
1170 secondarySize );
1171 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1172 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1173 }
1175 {
1176 InkAction* inky = ink_action_new( "NodeCurveAction",
1177 _("Node Curve"),
1178 _("Make selected segments curves"),
1179 "node_curve",
1180 secondarySize );
1181 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1182 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1183 }
1185 {
1186 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1187 _("Show Handles"),
1188 _("Show the Bezier handles of selected nodes"),
1189 "nodes_show_handles",
1190 Inkscape::ICON_SIZE_DECORATION );
1191 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1192 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1193 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1194 }
1196 {
1197 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1198 _("Show Outline"),
1199 _("Show the outline of the path"),
1200 "nodes_show_helperpath",
1201 Inkscape::ICON_SIZE_DECORATION );
1202 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1203 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1204 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1205 }
1207 {
1208 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1209 _("Next path effect parameter"),
1210 _("Show next path effect parameter for editing"),
1211 "edit_next_parameter",
1212 Inkscape::ICON_SIZE_DECORATION );
1213 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1214 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1215 g_object_set_data( holder, "nodes_lpeedit", inky);
1216 }
1218 {
1219 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1220 _("Edit clipping path"),
1221 _("Edit the clipping path of the object"),
1222 "nodeedit-clippath",
1223 Inkscape::ICON_SIZE_DECORATION );
1224 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1225 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1226 g_object_set_data( holder, "nodes_clippathedit", inky);
1227 }
1229 {
1230 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1231 _("Edit mask path"),
1232 _("Edit the mask of the object"),
1233 "nodeedit-mask",
1234 Inkscape::ICON_SIZE_DECORATION );
1235 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1236 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1237 g_object_set_data( holder, "nodes_maskedit", inky);
1238 }
1240 /* X coord of selected node(s) */
1241 {
1242 EgeAdjustmentAction* eact = 0;
1243 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1244 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1245 eact = create_adjustment_action( "NodeXAction",
1246 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1247 "tools.nodes", "Xcoord", 0,
1248 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1249 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1250 labels, values, G_N_ELEMENTS(labels),
1251 sp_node_path_x_value_changed );
1252 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1253 g_object_set_data( holder, "nodes_x_action", eact );
1254 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1255 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1256 }
1258 /* Y coord of selected node(s) */
1259 {
1260 EgeAdjustmentAction* eact = 0;
1261 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1262 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1263 eact = create_adjustment_action( "NodeYAction",
1264 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1265 "tools.nodes", "Ycoord", 0,
1266 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1267 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1268 labels, values, G_N_ELEMENTS(labels),
1269 sp_node_path_y_value_changed );
1270 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1271 g_object_set_data( holder, "nodes_y_action", eact );
1272 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1273 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1274 }
1276 // add the units menu
1277 {
1278 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1279 gtk_action_group_add_action( mainActions, act );
1280 }
1283 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1285 //watch selection
1286 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1288 sigc::connection *c_selection_changed =
1289 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1290 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1291 pool->add_connection ("selection-changed", c_selection_changed);
1293 sigc::connection *c_selection_modified =
1294 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1295 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1296 pool->add_connection ("selection-modified", c_selection_modified);
1298 sigc::connection *c_subselection_changed =
1299 new sigc::connection (desktop->connectToolSubselectionChanged
1300 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1301 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1303 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1305 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1306 } // end of sp_node_toolbox_prep()
1309 //########################
1310 //## Zoom Toolbox ##
1311 //########################
1313 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1314 {
1315 // no custom GtkAction setup needed
1316 } // end of sp_zoom_toolbox_prep()
1318 void
1319 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1320 {
1321 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")));
1322 }
1325 void
1326 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1327 {
1328 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")));
1329 }
1331 void
1332 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1333 {
1334 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")));
1335 }
1337 static void
1338 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1339 {
1340 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1341 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1343 if (old_desktop) {
1344 GList *children, *iter;
1346 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1347 for ( iter = children ; iter ; iter = iter->next ) {
1348 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1349 }
1350 g_list_free(children);
1351 }
1353 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1355 if (desktop) {
1356 gtk_widget_set_sensitive(toolbox, TRUE);
1357 setup_func(toolbox, desktop);
1358 update_func(desktop, desktop->event_context, toolbox);
1359 *conn = desktop->connectEventContextChanged
1360 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1361 } else {
1362 gtk_widget_set_sensitive(toolbox, FALSE);
1363 }
1365 } // end of toolbox_set_desktop()
1368 static void
1369 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1370 {
1371 GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1372 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1374 for (int i = 0 ; tools[i].type_name ; i++ ) {
1375 GtkWidget *button =
1376 sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1377 SP_BUTTON_TYPE_TOGGLE,
1378 Inkscape::Verb::get(tools[i].verb),
1379 Inkscape::Verb::get(tools[i].doubleclick_verb),
1380 desktop,
1381 tooltips );
1383 g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1384 (gpointer)button );
1385 }
1386 }
1389 static void
1390 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1391 {
1392 gchar const *const tname = ( eventcontext
1393 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1394 : NULL );
1395 for (int i = 0 ; tools[i].type_name ; i++ ) {
1396 SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1397 sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1398 }
1399 }
1401 static void
1402 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1403 {
1404 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1405 GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1406 GtkUIManager* mgr = gtk_ui_manager_new();
1407 GError* errVal = 0;
1408 gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1409 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1411 std::map<std::string, GtkWidget*> dataHolders;
1413 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1414 if ( aux_toolboxes[i].prep_func ) {
1415 // converted to GtkActions and UIManager
1417 GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1418 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1419 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1420 dataHolders[aux_toolboxes[i].type_name] = kludge;
1421 aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1422 } else {
1424 GtkWidget *sub_toolbox = 0;
1425 if (aux_toolboxes[i].create_func == NULL)
1426 sub_toolbox = sp_empty_toolbox_new(desktop);
1427 else {
1428 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1429 }
1431 gtk_size_group_add_widget( grouper, sub_toolbox );
1433 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1434 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1436 }
1437 }
1439 // Second pass to create toolbars *after* all GtkActions are created
1440 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1441 if ( aux_toolboxes[i].prep_func ) {
1442 // converted to GtkActions and UIManager
1444 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1446 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1447 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1449 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1450 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1451 g_free( tmp );
1452 tmp = 0;
1454 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1455 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1456 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1457 }
1458 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1461 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1463 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1464 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1465 swatch->setDesktop( desktop );
1466 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1467 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1468 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1469 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 );
1470 }
1472 gtk_widget_show_all( holder );
1473 sp_set_font_size_smaller( holder );
1475 gtk_size_group_add_widget( grouper, holder );
1477 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1478 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1479 }
1480 }
1482 g_object_unref( G_OBJECT(grouper) );
1483 }
1485 static void
1486 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1487 {
1488 gchar const *tname = ( eventcontext
1489 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1490 : NULL );
1491 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1492 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1493 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1494 gtk_widget_show_all(sub_toolbox);
1495 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1496 } else {
1497 gtk_widget_hide(sub_toolbox);
1498 }
1499 }
1500 }
1502 static void
1503 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1504 {
1505 gchar const * descr =
1506 "<ui>"
1507 " <toolbar name='CommandsToolbar'>"
1508 " <toolitem action='FileNew' />"
1509 " <toolitem action='FileOpen' />"
1510 " <toolitem action='FileSave' />"
1511 " <toolitem action='FilePrint' />"
1512 " <separator />"
1513 " <toolitem action='FileImport' />"
1514 " <toolitem action='FileExport' />"
1515 " <separator />"
1516 " <toolitem action='EditUndo' />"
1517 " <toolitem action='EditRedo' />"
1518 " <separator />"
1519 " <toolitem action='EditCopy' />"
1520 " <toolitem action='EditCut' />"
1521 " <toolitem action='EditPaste' />"
1522 " <separator />"
1523 " <toolitem action='ZoomSelection' />"
1524 " <toolitem action='ZoomDrawing' />"
1525 " <toolitem action='ZoomPage' />"
1526 " <separator />"
1527 " <toolitem action='EditDuplicate' />"
1528 " <toolitem action='EditClone' />"
1529 " <toolitem action='EditUnlinkClone' />"
1530 " <separator />"
1531 " <toolitem action='SelectionGroup' />"
1532 " <toolitem action='SelectionUnGroup' />"
1533 " <separator />"
1534 " <toolitem action='DialogFillStroke' />"
1535 " <toolitem action='DialogText' />"
1536 " <toolitem action='DialogXMLEditor' />"
1537 " <toolitem action='DialogAlignDistribute' />"
1538 " <separator />"
1539 " <toolitem action='DialogPreferences' />"
1540 " <toolitem action='DialogDocumentProperties' />"
1541 " </toolbar>"
1542 "</ui>";
1543 GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1546 GtkUIManager* mgr = gtk_ui_manager_new();
1547 GError* errVal = 0;
1549 gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1550 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1552 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1553 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1554 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1555 }
1557 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1558 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1561 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1562 }
1564 static void
1565 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1566 {
1567 }
1569 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1570 {
1571 gtk_widget_show(toolbox_toplevel);
1572 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1574 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1575 if (!shown_toolbox) {
1576 return;
1577 }
1578 gtk_widget_show(toolbox);
1580 gtk_widget_show_all(shown_toolbox);
1581 }
1583 void
1584 aux_toolbox_space(GtkWidget *tb, gint space)
1585 {
1586 gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1587 }
1589 static GtkWidget *
1590 sp_empty_toolbox_new(SPDesktop *desktop)
1591 {
1592 GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1593 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1594 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1596 gtk_widget_show_all(tbl);
1597 sp_set_font_size_smaller (tbl);
1599 return tbl;
1600 }
1602 // helper UI functions
1604 GtkWidget *
1605 sp_tb_spinbutton(
1606 gchar *label, gchar const *tooltip,
1607 gchar const *path, gchar const *data, gdouble def,
1608 GtkWidget *us,
1609 GtkWidget *tbl,
1610 gboolean altx, gchar const *altx_mark,
1611 gdouble lower, gdouble upper, gdouble step, gdouble page,
1612 void (*callback)(GtkAdjustment *, GtkWidget *),
1613 gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1614 {
1615 GtkTooltips *tt = gtk_tooltips_new();
1617 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1619 GtkWidget *l = gtk_label_new(label);
1620 gtk_widget_show(l);
1621 gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1622 gtk_container_add(GTK_CONTAINER(hb), l);
1624 GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1625 lower, upper, step, page, page);
1626 gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1627 if (us)
1628 sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1630 GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1631 gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1632 if (altx)
1633 gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1634 gtk_widget_set_size_request(sb,
1635 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1636 AUX_SPINBUTTON_HEIGHT);
1637 gtk_widget_show(sb);
1638 gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1639 gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1640 gtk_container_add(GTK_CONTAINER(hb), sb);
1641 gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1643 return hb;
1644 }
1646 #define MODE_LABEL_WIDTH 70
1648 //########################
1649 //## Star ##
1650 //########################
1652 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1653 {
1654 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1656 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1657 // do not remember prefs if this call is initiated by an undo change, because undoing object
1658 // creation sets bogus values to its attributes before it is deleted
1659 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1660 }
1662 // quit if run by the attr_changed listener
1663 if (g_object_get_data( dataKludge, "freeze" )) {
1664 return;
1665 }
1667 // in turn, prevent listener from responding
1668 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1670 bool modmade = false;
1672 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1673 GSList const *items = selection->itemList();
1674 for (; items != NULL; items = items->next) {
1675 if (SP_IS_STAR((SPItem *) items->data)) {
1676 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1677 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1678 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1679 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1680 + M_PI / (gint)adj->value));
1681 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1682 modmade = true;
1683 }
1684 }
1685 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1686 _("Star: Change number of corners"));
1688 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1689 }
1691 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1692 {
1693 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1695 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1696 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1697 }
1699 // quit if run by the attr_changed listener
1700 if (g_object_get_data( dataKludge, "freeze" )) {
1701 return;
1702 }
1704 // in turn, prevent listener from responding
1705 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1707 bool modmade = false;
1708 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1709 GSList const *items = selection->itemList();
1710 for (; items != NULL; items = items->next) {
1711 if (SP_IS_STAR((SPItem *) items->data)) {
1712 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1714 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1715 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1716 if (r2 < r1) {
1717 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1718 } else {
1719 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1720 }
1722 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1723 modmade = true;
1724 }
1725 }
1727 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1728 _("Star: Change spoke ratio"));
1730 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1731 }
1733 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1734 {
1735 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1736 bool flat = ege_select_one_action_get_active( act ) == 0;
1738 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1739 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1740 flat ? "true" : "false" );
1741 }
1743 // quit if run by the attr_changed listener
1744 if (g_object_get_data( dataKludge, "freeze" )) {
1745 return;
1746 }
1748 // in turn, prevent listener from responding
1749 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1751 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1752 GSList const *items = selection->itemList();
1753 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1754 bool modmade = false;
1756 if ( prop_action ) {
1757 gtk_action_set_sensitive( prop_action, !flat );
1758 }
1760 for (; items != NULL; items = items->next) {
1761 if (SP_IS_STAR((SPItem *) items->data)) {
1762 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1763 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1764 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1765 modmade = true;
1766 }
1767 }
1769 if (modmade) {
1770 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1771 flat ? _("Make polygon") : _("Make star"));
1772 }
1774 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1775 }
1777 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1778 {
1779 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1781 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1782 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1783 }
1785 // quit if run by the attr_changed listener
1786 if (g_object_get_data( dataKludge, "freeze" )) {
1787 return;
1788 }
1790 // in turn, prevent listener from responding
1791 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1793 bool modmade = false;
1795 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1796 GSList const *items = selection->itemList();
1797 for (; items != NULL; items = items->next) {
1798 if (SP_IS_STAR((SPItem *) items->data)) {
1799 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1800 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1801 SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1802 modmade = true;
1803 }
1804 }
1805 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1806 _("Star: Change rounding"));
1808 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1809 }
1811 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1812 {
1813 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1815 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1816 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1817 }
1819 // quit if run by the attr_changed listener
1820 if (g_object_get_data( dataKludge, "freeze" )) {
1821 return;
1822 }
1824 // in turn, prevent listener from responding
1825 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1827 bool modmade = false;
1829 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1830 GSList const *items = selection->itemList();
1831 for (; items != NULL; items = items->next) {
1832 if (SP_IS_STAR((SPItem *) items->data)) {
1833 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1834 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1835 SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1836 modmade = true;
1837 }
1838 }
1839 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1840 _("Star: Change randomization"));
1842 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1843 }
1846 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1847 gchar const */*old_value*/, gchar const */*new_value*/,
1848 bool /*is_interactive*/, gpointer data)
1849 {
1850 GtkWidget *tbl = GTK_WIDGET(data);
1852 // quit if run by the _changed callbacks
1853 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1854 return;
1855 }
1857 // in turn, prevent callbacks from responding
1858 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1860 GtkAdjustment *adj = 0;
1862 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1863 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1865 if (!strcmp(name, "inkscape:randomized")) {
1866 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1867 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1868 } else if (!strcmp(name, "inkscape:rounded")) {
1869 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1870 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1871 } else if (!strcmp(name, "inkscape:flatsided")) {
1872 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1873 char const *flatsides = repr->attribute("inkscape:flatsided");
1874 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1875 if ( flatsides && !strcmp(flatsides,"false") ) {
1876 ege_select_one_action_set_active( flat_action, 1 );
1877 gtk_action_set_sensitive( prop_action, TRUE );
1878 } else {
1879 ege_select_one_action_set_active( flat_action, 0 );
1880 gtk_action_set_sensitive( prop_action, FALSE );
1881 }
1882 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1883 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1884 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1885 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1886 if (r2 < r1) {
1887 gtk_adjustment_set_value(adj, r2/r1);
1888 } else {
1889 gtk_adjustment_set_value(adj, r1/r2);
1890 }
1891 } else if (!strcmp(name, "sodipodi:sides")) {
1892 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1893 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1894 }
1896 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1897 }
1900 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1901 {
1902 NULL, /* child_added */
1903 NULL, /* child_removed */
1904 star_tb_event_attr_changed,
1905 NULL, /* content_changed */
1906 NULL /* order_changed */
1907 };
1910 /**
1911 * \param selection Should not be NULL.
1912 */
1913 static void
1914 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1915 {
1916 int n_selected = 0;
1917 Inkscape::XML::Node *repr = NULL;
1919 purge_repr_listener( tbl, tbl );
1921 for (GSList const *items = selection->itemList();
1922 items != NULL;
1923 items = items->next)
1924 {
1925 if (SP_IS_STAR((SPItem *) items->data)) {
1926 n_selected++;
1927 repr = SP_OBJECT_REPR((SPItem *) items->data);
1928 }
1929 }
1931 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1933 if (n_selected == 0) {
1934 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1935 } else if (n_selected == 1) {
1936 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1938 if (repr) {
1939 g_object_set_data( tbl, "repr", repr );
1940 Inkscape::GC::anchor(repr);
1941 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1942 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1943 }
1944 } else {
1945 // FIXME: implement averaging of all parameters for multiple selected stars
1946 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1947 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1948 }
1949 }
1952 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1953 {
1954 // FIXME: in this and all other _default functions, set some flag telling the value_changed
1955 // callbacks to lump all the changes for all selected objects in one undo step
1957 GtkAdjustment *adj = 0;
1959 // fixme: make settable in prefs!
1960 gint mag = 5;
1961 gdouble prop = 0.5;
1962 gboolean flat = FALSE;
1963 gdouble randomized = 0;
1964 gdouble rounded = 0;
1966 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1967 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1969 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1970 gtk_action_set_sensitive( sb2, !flat );
1972 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1973 gtk_adjustment_set_value(adj, mag);
1974 gtk_adjustment_value_changed(adj);
1976 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1977 gtk_adjustment_set_value(adj, prop);
1978 gtk_adjustment_value_changed(adj);
1980 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1981 gtk_adjustment_set_value(adj, rounded);
1982 gtk_adjustment_value_changed(adj);
1984 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1985 gtk_adjustment_set_value(adj, randomized);
1986 gtk_adjustment_value_changed(adj);
1987 }
1990 void
1991 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1992 {
1993 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1994 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1995 GtkWidget *l = gtk_label_new(NULL);
1996 gtk_label_set_markup(GTK_LABEL(l), title);
1997 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1998 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1999 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2000 }
2003 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2004 {
2005 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2007 {
2008 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2009 ege_output_action_set_use_markup( act, TRUE );
2010 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2011 g_object_set_data( holder, "mode_action", act );
2012 }
2014 {
2015 EgeAdjustmentAction* eact = 0;
2016 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2017 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2019 /* Flatsided checkbox */
2020 {
2021 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2023 GtkTreeIter iter;
2024 gtk_list_store_append( model, &iter );
2025 gtk_list_store_set( model, &iter,
2026 0, _("Polygon"),
2027 1, _("Regular polygon (with one handle) instead of a star"),
2028 2, "star_flat",
2029 -1 );
2031 gtk_list_store_append( model, &iter );
2032 gtk_list_store_set( model, &iter,
2033 0, _("Star"),
2034 1, _("Star instead of a regular polygon (with one handle)"),
2035 2, "star_angled",
2036 -1 );
2038 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2039 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2040 g_object_set_data( holder, "flat_action", act );
2042 ege_select_one_action_set_appearance( act, "full" );
2043 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2044 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2045 ege_select_one_action_set_icon_column( act, 2 );
2046 ege_select_one_action_set_icon_size( act, secondarySize );
2047 ege_select_one_action_set_tooltip_column( act, 1 );
2049 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2050 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2051 }
2053 /* Magnitude */
2054 {
2055 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2056 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2057 eact = create_adjustment_action( "MagnitudeAction",
2058 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2059 "tools.shapes.star", "magnitude", 3,
2060 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2061 3, 1024, 1, 5,
2062 labels, values, G_N_ELEMENTS(labels),
2063 sp_stb_magnitude_value_changed,
2064 1.0, 0 );
2065 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2066 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2067 }
2069 /* Spoke ratio */
2070 {
2071 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2072 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2073 eact = create_adjustment_action( "SpokeAction",
2074 _("Spoke ratio"), _("Spoke ratio:"),
2075 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2076 // Base radius is the same for the closest handle.
2077 _("Base radius to tip radius ratio"),
2078 "tools.shapes.star", "proportion", 0.5,
2079 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2080 0.01, 1.0, 0.01, 0.1,
2081 labels, values, G_N_ELEMENTS(labels),
2082 sp_stb_proportion_value_changed );
2083 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2084 g_object_set_data( holder, "prop_action", eact );
2085 }
2087 if ( !isFlatSided ) {
2088 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2089 } else {
2090 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2091 }
2093 /* Roundedness */
2094 {
2095 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2096 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2097 eact = create_adjustment_action( "RoundednessAction",
2098 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2099 "tools.shapes.star", "rounded", 0.0,
2100 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2101 -10.0, 10.0, 0.01, 0.1,
2102 labels, values, G_N_ELEMENTS(labels),
2103 sp_stb_rounded_value_changed );
2104 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2105 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2106 }
2108 /* Randomization */
2109 {
2110 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2111 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2112 eact = create_adjustment_action( "RandomizationAction",
2113 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2114 "tools.shapes.star", "randomized", 0.0,
2115 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2116 -10.0, 10.0, 0.001, 0.01,
2117 labels, values, G_N_ELEMENTS(labels),
2118 sp_stb_randomized_value_changed, 0.1, 3 );
2119 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2120 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2121 }
2122 }
2124 {
2125 /* Reset */
2126 {
2127 GtkAction* act = gtk_action_new( "StarResetAction",
2128 _("Defaults"),
2129 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2130 GTK_STOCK_CLEAR );
2131 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2132 gtk_action_group_add_action( mainActions, act );
2133 gtk_action_set_sensitive( act, TRUE );
2134 }
2135 }
2137 sigc::connection *connection = new sigc::connection(
2138 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2139 );
2140 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2141 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2142 }
2145 //########################
2146 //## Rect ##
2147 //########################
2149 static void sp_rtb_sensitivize( GObject *tbl )
2150 {
2151 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2152 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2153 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2155 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2156 gtk_action_set_sensitive( not_rounded, FALSE );
2157 } else {
2158 gtk_action_set_sensitive( not_rounded, TRUE );
2159 }
2160 }
2163 static void
2164 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2165 void (*setter)(SPRect *, gdouble))
2166 {
2167 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2169 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2170 SPUnit const *unit = tracker->getActiveUnit();
2172 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2173 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2174 }
2176 // quit if run by the attr_changed listener
2177 if (g_object_get_data( tbl, "freeze" )) {
2178 return;
2179 }
2181 // in turn, prevent listener from responding
2182 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2184 bool modmade = false;
2185 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2186 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2187 if (SP_IS_RECT(items->data)) {
2188 if (adj->value != 0) {
2189 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2190 } else {
2191 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2192 }
2193 modmade = true;
2194 }
2195 }
2197 sp_rtb_sensitivize( tbl );
2199 if (modmade) {
2200 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2201 _("Change rectangle"));
2202 }
2204 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2205 }
2207 static void
2208 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2209 {
2210 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2211 }
2213 static void
2214 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2215 {
2216 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2217 }
2219 static void
2220 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2221 {
2222 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2223 }
2225 static void
2226 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2227 {
2228 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2229 }
2233 static void
2234 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2235 {
2236 GtkAdjustment *adj = 0;
2238 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2239 gtk_adjustment_set_value(adj, 0.0);
2240 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2241 gtk_adjustment_value_changed(adj);
2243 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2244 gtk_adjustment_set_value(adj, 0.0);
2245 gtk_adjustment_value_changed(adj);
2247 sp_rtb_sensitivize( obj );
2248 }
2250 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2251 gchar const */*old_value*/, gchar const */*new_value*/,
2252 bool /*is_interactive*/, gpointer data)
2253 {
2254 GObject *tbl = G_OBJECT(data);
2256 // quit if run by the _changed callbacks
2257 if (g_object_get_data( tbl, "freeze" )) {
2258 return;
2259 }
2261 // in turn, prevent callbacks from responding
2262 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2264 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2265 SPUnit const *unit = tracker->getActiveUnit();
2267 gpointer item = g_object_get_data( tbl, "item" );
2268 if (item && SP_IS_RECT(item)) {
2269 {
2270 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2271 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2272 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2273 }
2275 {
2276 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2277 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2278 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2279 }
2281 {
2282 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2283 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2284 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2285 }
2287 {
2288 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2289 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2290 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2291 }
2292 }
2294 sp_rtb_sensitivize( tbl );
2296 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2297 }
2300 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2301 NULL, /* child_added */
2302 NULL, /* child_removed */
2303 rect_tb_event_attr_changed,
2304 NULL, /* content_changed */
2305 NULL /* order_changed */
2306 };
2308 /**
2309 * \param selection should not be NULL.
2310 */
2311 static void
2312 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2313 {
2314 int n_selected = 0;
2315 Inkscape::XML::Node *repr = NULL;
2316 SPItem *item = NULL;
2318 if ( g_object_get_data( tbl, "repr" ) ) {
2319 g_object_set_data( tbl, "item", NULL );
2320 }
2321 purge_repr_listener( tbl, tbl );
2323 for (GSList const *items = selection->itemList();
2324 items != NULL;
2325 items = items->next) {
2326 if (SP_IS_RECT((SPItem *) items->data)) {
2327 n_selected++;
2328 item = (SPItem *) items->data;
2329 repr = SP_OBJECT_REPR(item);
2330 }
2331 }
2333 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2335 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2337 if (n_selected == 0) {
2338 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2340 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2341 gtk_action_set_sensitive(w, FALSE);
2342 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2343 gtk_action_set_sensitive(h, FALSE);
2345 } else if (n_selected == 1) {
2346 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2347 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2349 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2350 gtk_action_set_sensitive(w, TRUE);
2351 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2352 gtk_action_set_sensitive(h, TRUE);
2354 if (repr) {
2355 g_object_set_data( tbl, "repr", repr );
2356 g_object_set_data( tbl, "item", item );
2357 Inkscape::GC::anchor(repr);
2358 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2359 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2360 }
2361 } else {
2362 // FIXME: implement averaging of all parameters for multiple selected
2363 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2364 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2365 sp_rtb_sensitivize( tbl );
2366 }
2367 }
2370 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2371 {
2372 EgeAdjustmentAction* eact = 0;
2373 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2375 {
2376 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2377 ege_output_action_set_use_markup( act, TRUE );
2378 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2379 g_object_set_data( holder, "mode_action", act );
2380 }
2382 // rx/ry units menu: create
2383 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2384 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2385 // fixme: add % meaning per cent of the width/height
2386 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2387 g_object_set_data( holder, "tracker", tracker );
2389 /* W */
2390 {
2391 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2392 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2393 eact = create_adjustment_action( "RectWidthAction",
2394 _("Width"), _("W:"), _("Width of rectangle"),
2395 "tools.shapes.rect", "width", 0,
2396 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2397 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2398 labels, values, G_N_ELEMENTS(labels),
2399 sp_rtb_width_value_changed );
2400 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2401 g_object_set_data( holder, "width_action", eact );
2402 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2403 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2404 }
2406 /* H */
2407 {
2408 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2409 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2410 eact = create_adjustment_action( "RectHeightAction",
2411 _("Height"), _("H:"), _("Height of rectangle"),
2412 "tools.shapes.rect", "height", 0,
2413 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2414 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2415 labels, values, G_N_ELEMENTS(labels),
2416 sp_rtb_height_value_changed );
2417 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2418 g_object_set_data( holder, "height_action", eact );
2419 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2420 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2421 }
2423 /* rx */
2424 {
2425 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2426 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2427 eact = create_adjustment_action( "RadiusXAction",
2428 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2429 "tools.shapes.rect", "rx", 0,
2430 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2431 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2432 labels, values, G_N_ELEMENTS(labels),
2433 sp_rtb_rx_value_changed);
2434 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2435 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2436 }
2438 /* ry */
2439 {
2440 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2441 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2442 eact = create_adjustment_action( "RadiusYAction",
2443 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2444 "tools.shapes.rect", "ry", 0,
2445 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2446 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2447 labels, values, G_N_ELEMENTS(labels),
2448 sp_rtb_ry_value_changed);
2449 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2450 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2451 }
2453 // add the units menu
2454 {
2455 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2456 gtk_action_group_add_action( mainActions, act );
2457 }
2459 /* Reset */
2460 {
2461 InkAction* inky = ink_action_new( "RectResetAction",
2462 _("Not rounded"),
2463 _("Make corners sharp"),
2464 "squared_corner",
2465 secondarySize );
2466 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2467 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2468 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2469 g_object_set_data( holder, "not_rounded", inky );
2470 }
2472 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2473 sp_rtb_sensitivize( holder );
2475 sigc::connection *connection = new sigc::connection(
2476 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2477 );
2478 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2479 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2480 }
2482 //########################
2483 //## 3D Box ##
2484 //########################
2486 // normalize angle so that it lies in the interval [0,360]
2487 static double box3d_normalize_angle (double a) {
2488 double angle = a + ((int) (a/360.0))*360;
2489 if (angle < 0) {
2490 angle += 360.0;
2491 }
2492 return angle;
2493 }
2495 static void
2496 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2497 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2498 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2499 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2500 // are reset).
2501 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2503 if (is_infinite) {
2504 gtk_toggle_action_set_active(tact, TRUE);
2505 gtk_action_set_sensitive(act, TRUE);
2507 double angle = persp3d_get_infinite_angle(persp, axis);
2508 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2509 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2510 }
2511 } else {
2512 gtk_toggle_action_set_active(tact, FALSE);
2513 gtk_action_set_sensitive(act, FALSE);
2514 }
2515 }
2517 static void
2518 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2519 if (!persp_repr) {
2520 g_print ("No perspective given to box3d_resync_toolbar().\n");
2521 return;
2522 }
2524 GtkWidget *tbl = GTK_WIDGET(data);
2525 GtkAdjustment *adj = 0;
2526 GtkAction *act = 0;
2527 GtkToggleAction *tact = 0;
2528 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2529 {
2530 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2531 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2532 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2534 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2535 }
2536 {
2537 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2538 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2539 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2541 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2542 }
2543 {
2544 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2545 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2546 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2548 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2549 }
2550 }
2552 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2553 gchar const */*old_value*/, gchar const */*new_value*/,
2554 bool /*is_interactive*/, gpointer data)
2555 {
2556 GtkWidget *tbl = GTK_WIDGET(data);
2558 // quit if run by the attr_changed listener
2559 // note: it used to work without the differently called freeze_ attributes (here and in
2560 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2561 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2562 return;
2563 }
2565 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2566 // sp_document_maybe_done() when the document is undo insensitive)
2567 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2569 // TODO: Only update the appropriate part of the toolbar
2570 // if (!strcmp(name, "inkscape:vp_z")) {
2571 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2572 // }
2574 Persp3D *persp = persp3d_get_from_repr(repr);
2575 persp3d_update_box_reprs(persp);
2577 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2578 }
2580 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2581 {
2582 NULL, /* child_added */
2583 NULL, /* child_removed */
2584 box3d_persp_tb_event_attr_changed,
2585 NULL, /* content_changed */
2586 NULL /* order_changed */
2587 };
2589 /**
2590 * \param selection Should not be NULL.
2591 */
2592 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2593 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2594 static void
2595 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2596 {
2597 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2598 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2599 // update the perspectives with infinite VPs and leave the other ones untouched).
2601 Inkscape::XML::Node *persp_repr = NULL;
2602 purge_repr_listener(tbl, tbl);
2604 SPItem *item = selection->singleItem();
2605 if (item && SP_IS_BOX3D(item)) {
2606 // FIXME: Also deal with multiple selected boxes
2607 SPBox3D *box = SP_BOX3D(item);
2608 Persp3D *persp = box3d_get_perspective(box);
2609 persp_repr = SP_OBJECT_REPR(persp);
2610 if (persp_repr) {
2611 g_object_set_data(tbl, "repr", persp_repr);
2612 Inkscape::GC::anchor(persp_repr);
2613 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2614 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2615 }
2617 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2618 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2620 box3d_resync_toolbar(persp_repr, tbl);
2621 }
2622 }
2624 static void
2625 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2626 {
2627 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2628 SPDocument *document = sp_desktop_document(desktop);
2630 // quit if run by the attr_changed listener
2631 // note: it used to work without the differently called freeze_ attributes (here and in
2632 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2633 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2634 return;
2635 }
2637 // in turn, prevent listener from responding
2638 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2640 //Persp3D *persp = document->current_persp3d;
2641 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2642 if (sel_persps.empty()) {
2643 // this can happen when the document is created; we silently ignore it
2644 return;
2645 }
2646 Persp3D *persp = sel_persps.front();
2648 persp->tmat.set_infinite_direction (axis, adj->value);
2649 SP_OBJECT(persp)->updateRepr();
2651 // TODO: use the correct axis here, too
2652 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2654 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2655 }
2658 static void
2659 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2660 {
2661 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2662 }
2664 static void
2665 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2666 {
2667 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2668 }
2670 static void
2671 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2672 {
2673 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2674 }
2677 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2678 {
2679 // TODO: Take all selected perspectives into account
2680 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2681 if (sel_persps.empty()) {
2682 // this can happen when the document is created; we silently ignore it
2683 return;
2684 }
2685 Persp3D *persp = sel_persps.front();
2687 bool set_infinite = gtk_toggle_action_get_active(act);
2688 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2689 }
2691 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2692 {
2693 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2694 }
2696 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2697 {
2698 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2699 }
2701 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2702 {
2703 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2704 }
2706 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2707 {
2708 EgeAdjustmentAction* eact = 0;
2709 SPDocument *document = sp_desktop_document (desktop);
2710 Persp3D *persp = document->current_persp3d;
2712 EgeAdjustmentAction* box3d_angle_x = 0;
2713 EgeAdjustmentAction* box3d_angle_y = 0;
2714 EgeAdjustmentAction* box3d_angle_z = 0;
2716 /* Angle X */
2717 {
2718 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2719 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2720 eact = create_adjustment_action( "3DBoxAngleXAction",
2721 _("Angle in X direction"), _("Angle X:"),
2722 // Translators: PL is short for 'perspective line'
2723 _("Angle of PLs in X direction"),
2724 "tools.shapes.3dbox", "box3d_angle_x", 30,
2725 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2726 -360.0, 360.0, 1.0, 10.0,
2727 labels, values, G_N_ELEMENTS(labels),
2728 box3d_angle_x_value_changed );
2729 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2730 g_object_set_data( holder, "box3d_angle_x_action", eact );
2731 box3d_angle_x = eact;
2732 }
2734 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2735 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2736 } else {
2737 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2738 }
2741 /* VP X state */
2742 {
2743 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2744 // Translators: VP is short for 'vanishing point'
2745 _("State of VP in X direction"),
2746 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2747 "toggle_vp_x",
2748 Inkscape::ICON_SIZE_DECORATION );
2749 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2750 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2751 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2752 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2753 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2754 }
2756 /* Angle Y */
2757 {
2758 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2759 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2760 eact = create_adjustment_action( "3DBoxAngleYAction",
2761 _("Angle in Y direction"), _("Angle Y:"),
2762 // Translators: PL is short for 'perspective line'
2763 _("Angle of PLs in Y direction"),
2764 "tools.shapes.3dbox", "box3d_angle_y", 30,
2765 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2766 -360.0, 360.0, 1.0, 10.0,
2767 labels, values, G_N_ELEMENTS(labels),
2768 box3d_angle_y_value_changed );
2769 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2770 g_object_set_data( holder, "box3d_angle_y_action", eact );
2771 box3d_angle_y = eact;
2772 }
2774 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2775 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2776 } else {
2777 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2778 }
2780 /* VP Y state */
2781 {
2782 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2783 // Translators: VP is short for 'vanishing point'
2784 _("State of VP in Y direction"),
2785 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2786 "toggle_vp_y",
2787 Inkscape::ICON_SIZE_DECORATION );
2788 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2789 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2790 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2791 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2792 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2793 }
2795 /* Angle Z */
2796 {
2797 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2798 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2799 eact = create_adjustment_action( "3DBoxAngleZAction",
2800 _("Angle in Z direction"), _("Angle Z:"),
2801 // Translators: PL is short for 'perspective line'
2802 _("Angle of PLs in Z direction"),
2803 "tools.shapes.3dbox", "box3d_angle_z", 30,
2804 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2805 -360.0, 360.0, 1.0, 10.0,
2806 labels, values, G_N_ELEMENTS(labels),
2807 box3d_angle_z_value_changed );
2808 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2809 g_object_set_data( holder, "box3d_angle_z_action", eact );
2810 box3d_angle_z = eact;
2811 }
2813 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2814 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2815 } else {
2816 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2817 }
2819 /* VP Z state */
2820 {
2821 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2822 // Translators: VP is short for 'vanishing point'
2823 _("State of VP in Z direction"),
2824 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2825 "toggle_vp_z",
2826 Inkscape::ICON_SIZE_DECORATION );
2827 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2828 g_object_set_data( holder, "box3d_vp_z_state_action", act );
2829 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2830 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2831 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2832 }
2834 sigc::connection *connection = new sigc::connection(
2835 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2836 );
2837 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2838 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2839 }
2841 //########################
2842 //## Spiral ##
2843 //########################
2845 static void
2846 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2847 {
2848 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2850 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2851 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2852 }
2854 // quit if run by the attr_changed listener
2855 if (g_object_get_data( tbl, "freeze" )) {
2856 return;
2857 }
2859 // in turn, prevent listener from responding
2860 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2862 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2864 bool modmade = false;
2865 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2866 items != NULL;
2867 items = items->next)
2868 {
2869 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2870 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2871 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2872 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2873 modmade = true;
2874 }
2875 }
2877 g_free(namespaced_name);
2879 if (modmade) {
2880 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2881 _("Change spiral"));
2882 }
2884 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2885 }
2887 static void
2888 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2889 {
2890 sp_spl_tb_value_changed(adj, tbl, "revolution");
2891 }
2893 static void
2894 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2895 {
2896 sp_spl_tb_value_changed(adj, tbl, "expansion");
2897 }
2899 static void
2900 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2901 {
2902 sp_spl_tb_value_changed(adj, tbl, "t0");
2903 }
2905 static void
2906 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2907 {
2908 GtkWidget *tbl = GTK_WIDGET(obj);
2910 GtkAdjustment *adj;
2912 // fixme: make settable
2913 gdouble rev = 5;
2914 gdouble exp = 1.0;
2915 gdouble t0 = 0.0;
2917 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2918 gtk_adjustment_set_value(adj, rev);
2919 gtk_adjustment_value_changed(adj);
2921 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2922 gtk_adjustment_set_value(adj, exp);
2923 gtk_adjustment_value_changed(adj);
2925 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2926 gtk_adjustment_set_value(adj, t0);
2927 gtk_adjustment_value_changed(adj);
2929 spinbutton_defocus(GTK_OBJECT(tbl));
2930 }
2933 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2934 gchar const */*old_value*/, gchar const */*new_value*/,
2935 bool /*is_interactive*/, gpointer data)
2936 {
2937 GtkWidget *tbl = GTK_WIDGET(data);
2939 // quit if run by the _changed callbacks
2940 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2941 return;
2942 }
2944 // in turn, prevent callbacks from responding
2945 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2947 GtkAdjustment *adj;
2948 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2949 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2951 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2952 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2954 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2955 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2957 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2958 }
2961 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2962 NULL, /* child_added */
2963 NULL, /* child_removed */
2964 spiral_tb_event_attr_changed,
2965 NULL, /* content_changed */
2966 NULL /* order_changed */
2967 };
2969 static void
2970 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2971 {
2972 int n_selected = 0;
2973 Inkscape::XML::Node *repr = NULL;
2975 purge_repr_listener( tbl, tbl );
2977 for (GSList const *items = selection->itemList();
2978 items != NULL;
2979 items = items->next)
2980 {
2981 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2982 n_selected++;
2983 repr = SP_OBJECT_REPR((SPItem *) items->data);
2984 }
2985 }
2987 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2989 if (n_selected == 0) {
2990 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2991 } else if (n_selected == 1) {
2992 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2994 if (repr) {
2995 g_object_set_data( tbl, "repr", repr );
2996 Inkscape::GC::anchor(repr);
2997 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2998 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2999 }
3000 } else {
3001 // FIXME: implement averaging of all parameters for multiple selected
3002 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3003 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3004 }
3005 }
3008 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3009 {
3010 EgeAdjustmentAction* eact = 0;
3011 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3013 {
3014 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3015 ege_output_action_set_use_markup( act, TRUE );
3016 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3017 g_object_set_data( holder, "mode_action", act );
3018 }
3020 /* Revolution */
3021 {
3022 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3023 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3024 eact = create_adjustment_action( "SpiralRevolutionAction",
3025 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3026 "tools.shapes.spiral", "revolution", 3.0,
3027 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3028 0.01, 1024.0, 0.1, 1.0,
3029 labels, values, G_N_ELEMENTS(labels),
3030 sp_spl_tb_revolution_value_changed, 1, 2);
3031 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3032 }
3034 /* Expansion */
3035 {
3036 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3037 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3038 eact = create_adjustment_action( "SpiralExpansionAction",
3039 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3040 "tools.shapes.spiral", "expansion", 1.0,
3041 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3042 0.0, 1000.0, 0.01, 1.0,
3043 labels, values, G_N_ELEMENTS(labels),
3044 sp_spl_tb_expansion_value_changed);
3045 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3046 }
3048 /* T0 */
3049 {
3050 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3051 gdouble values[] = {0, 0.5, 0.9};
3052 eact = create_adjustment_action( "SpiralT0Action",
3053 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3054 "tools.shapes.spiral", "t0", 0.0,
3055 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3056 0.0, 0.999, 0.01, 1.0,
3057 labels, values, G_N_ELEMENTS(labels),
3058 sp_spl_tb_t0_value_changed);
3059 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3060 }
3062 /* Reset */
3063 {
3064 InkAction* inky = ink_action_new( "SpiralResetAction",
3065 _("Defaults"),
3066 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3067 GTK_STOCK_CLEAR,
3068 secondarySize );
3069 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3070 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3071 }
3074 sigc::connection *connection = new sigc::connection(
3075 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3076 );
3077 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3078 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3079 }
3081 //########################
3082 //## Pen/Pencil ##
3083 //########################
3085 static void sp_pc_spiro_spline_mode_changed(GtkToggleAction *act, GObject* /*tbl*/)
3086 {
3087 prefs_set_int_attribute("tools.freehand", "spiro-spline-mode", gtk_toggle_action_get_active(act) ? 1 : 0);
3088 }
3090 static void sp_add_spiro_toggle(GtkActionGroup* mainActions, GObject* holder, const char* action_name)
3091 {
3092 /* Spiro Spline Mode toggle button */
3093 {
3094 InkToggleAction* act = ink_toggle_action_new(action_name,
3095 _("Spiro Spline Mode"),
3096 _("Automatically apply the 'Spiro Spline' live path effect to newly drawn paths"),
3097 "spiro_splines_mode",
3098 Inkscape::ICON_SIZE_DECORATION );
3099 gtk_action_group_add_action(mainActions, GTK_ACTION(act));
3100 g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(sp_pc_spiro_spline_mode_changed), holder);
3101 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), prefs_get_int_attribute("tools.freehand", "spiro-spline-mode", 0));
3102 g_object_set_data( holder, "spiro_spline_mode", act );
3103 }
3104 }
3106 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3107 {
3108 sp_add_spiro_toggle(mainActions, holder, "SpiroSplineModeActionPen");
3109 }
3111 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3112 {
3113 sp_add_spiro_toggle(mainActions, holder, "SpiroSplineModeActionPencil");
3114 }
3116 //########################
3117 //## Tweak ##
3118 //########################
3120 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3121 {
3122 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3123 }
3125 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3126 {
3127 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3128 }
3130 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3131 {
3132 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3133 }
3135 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3136 {
3137 int mode = ege_select_one_action_get_active( act );
3138 prefs_set_int_attribute("tools.tweak", "mode", mode);
3140 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3141 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3142 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3143 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3144 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3145 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3146 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3147 if (doh) gtk_action_set_sensitive (doh, TRUE);
3148 if (dos) gtk_action_set_sensitive (dos, TRUE);
3149 if (dol) gtk_action_set_sensitive (dol, TRUE);
3150 if (doo) gtk_action_set_sensitive (doo, TRUE);
3151 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3152 if (fid) gtk_action_set_sensitive (fid, FALSE);
3153 } else {
3154 if (doh) gtk_action_set_sensitive (doh, FALSE);
3155 if (dos) gtk_action_set_sensitive (dos, FALSE);
3156 if (dol) gtk_action_set_sensitive (dol, FALSE);
3157 if (doo) gtk_action_set_sensitive (doo, FALSE);
3158 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3159 if (fid) gtk_action_set_sensitive (fid, TRUE);
3160 }
3161 }
3163 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3164 {
3165 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3166 }
3168 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3169 bool show = gtk_toggle_action_get_active( act );
3170 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3171 }
3172 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3173 bool show = gtk_toggle_action_get_active( act );
3174 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3175 }
3176 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3177 bool show = gtk_toggle_action_get_active( act );
3178 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3179 }
3180 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3181 bool show = gtk_toggle_action_get_active( act );
3182 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3183 }
3185 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3186 {
3187 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3189 {
3190 /* Width */
3191 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3192 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3193 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3194 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3195 "tools.tweak", "width", 15,
3196 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3197 1, 100, 1.0, 10.0,
3198 labels, values, G_N_ELEMENTS(labels),
3199 sp_tweak_width_value_changed, 0.01, 0, 100 );
3200 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3201 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3202 }
3205 {
3206 /* Force */
3207 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3208 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3209 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3210 _("Force"), _("Force:"), _("The force of the tweak action"),
3211 "tools.tweak", "force", 20,
3212 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3213 1, 100, 1.0, 10.0,
3214 labels, values, G_N_ELEMENTS(labels),
3215 sp_tweak_force_value_changed, 0.01, 0, 100 );
3216 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3217 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3218 }
3220 /* Mode */
3221 {
3222 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3224 GtkTreeIter iter;
3225 gtk_list_store_append( model, &iter );
3226 gtk_list_store_set( model, &iter,
3227 0, _("Push mode"),
3228 1, _("Push parts of paths in any direction"),
3229 2, "tweak_push_mode",
3230 -1 );
3232 gtk_list_store_append( model, &iter );
3233 gtk_list_store_set( model, &iter,
3234 0, _("Shrink mode"),
3235 1, _("Shrink (inset) parts of paths"),
3236 2, "tweak_shrink_mode",
3237 -1 );
3239 gtk_list_store_append( model, &iter );
3240 gtk_list_store_set( model, &iter,
3241 0, _("Grow mode"),
3242 1, _("Grow (outset) parts of paths"),
3243 2, "tweak_grow_mode",
3244 -1 );
3246 gtk_list_store_append( model, &iter );
3247 gtk_list_store_set( model, &iter,
3248 0, _("Attract mode"),
3249 1, _("Attract parts of paths towards cursor"),
3250 2, "tweak_attract_mode",
3251 -1 );
3253 gtk_list_store_append( model, &iter );
3254 gtk_list_store_set( model, &iter,
3255 0, _("Repel mode"),
3256 1, _("Repel parts of paths from cursor"),
3257 2, "tweak_repel_mode",
3258 -1 );
3260 gtk_list_store_append( model, &iter );
3261 gtk_list_store_set( model, &iter,
3262 0, _("Roughen mode"),
3263 1, _("Roughen parts of paths"),
3264 2, "tweak_roughen_mode",
3265 -1 );
3267 gtk_list_store_append( model, &iter );
3268 gtk_list_store_set( model, &iter,
3269 0, _("Color paint mode"),
3270 1, _("Paint the tool's color upon selected objects"),
3271 2, "tweak_colorpaint_mode",
3272 -1 );
3274 gtk_list_store_append( model, &iter );
3275 gtk_list_store_set( model, &iter,
3276 0, _("Color jitter mode"),
3277 1, _("Jitter the colors of selected objects"),
3278 2, "tweak_colorjitter_mode",
3279 -1 );
3281 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3282 g_object_set( act, "short_label", _("Mode:"), NULL );
3283 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3284 g_object_set_data( holder, "mode_action", act );
3286 ege_select_one_action_set_appearance( act, "full" );
3287 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3288 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3289 ege_select_one_action_set_icon_column( act, 2 );
3290 ege_select_one_action_set_icon_size( act, secondarySize );
3291 ege_select_one_action_set_tooltip_column( act, 1 );
3293 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3294 ege_select_one_action_set_active( act, mode );
3295 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3297 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3298 }
3300 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3302 {
3303 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3304 ege_output_action_set_use_markup( act, TRUE );
3305 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3306 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3307 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3308 g_object_set_data( holder, "tweak_channels_label", act);
3309 }
3311 {
3312 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3313 _("Hue"),
3314 _("In color mode, act on objects' hue"),
3315 NULL,
3316 Inkscape::ICON_SIZE_DECORATION );
3317 //TRANSLATORS: "H" here stands for hue
3318 g_object_set( act, "short_label", _("H"), NULL );
3319 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3320 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3321 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3322 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3323 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3324 g_object_set_data( holder, "tweak_doh", act);
3325 }
3326 {
3327 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3328 _("Saturation"),
3329 _("In color mode, act on objects' saturation"),
3330 NULL,
3331 Inkscape::ICON_SIZE_DECORATION );
3332 //TRANSLATORS: "S" here stands for Saturation
3333 g_object_set( act, "short_label", _("S"), NULL );
3334 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3335 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3336 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3337 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3338 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3339 g_object_set_data( holder, "tweak_dos", act );
3340 }
3341 {
3342 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3343 _("Lightness"),
3344 _("In color mode, act on objects' lightness"),
3345 NULL,
3346 Inkscape::ICON_SIZE_DECORATION );
3347 //TRANSLATORS: "L" here stands for Lightness
3348 g_object_set( act, "short_label", _("L"), NULL );
3349 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3350 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3351 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3352 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3353 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3354 g_object_set_data( holder, "tweak_dol", act );
3355 }
3356 {
3357 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3358 _("Opacity"),
3359 _("In color mode, act on objects' opacity"),
3360 NULL,
3361 Inkscape::ICON_SIZE_DECORATION );
3362 //TRANSLATORS: "O" here stands for Opacity
3363 g_object_set( act, "short_label", _("O"), NULL );
3364 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3365 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3366 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3367 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3368 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3369 g_object_set_data( holder, "tweak_doo", act );
3370 }
3372 { /* Fidelity */
3373 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3374 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3375 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3376 _("Fidelity"), _("Fidelity:"),
3377 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3378 "tools.tweak", "fidelity", 50,
3379 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3380 1, 100, 1.0, 10.0,
3381 labels, values, G_N_ELEMENTS(labels),
3382 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3383 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3384 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3385 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3386 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3387 g_object_set_data( holder, "tweak_fidelity", eact );
3388 }
3391 /* Use Pressure button */
3392 {
3393 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3394 _("Pressure"),
3395 _("Use the pressure of the input device to alter the force of tweak action"),
3396 "use_pressure",
3397 Inkscape::ICON_SIZE_DECORATION );
3398 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3399 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3400 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3401 }
3403 }
3406 //########################
3407 //## Calligraphy ##
3408 //########################
3409 static void update_presets_list(GObject *dataKludge ){
3410 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3411 if (sel) {
3412 ege_select_one_action_set_active(sel, 0 );
3413 }
3414 }
3416 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3417 {
3418 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3419 update_presets_list(tbl);
3420 }
3422 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3423 {
3424 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3425 update_presets_list(tbl);
3426 }
3428 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3429 {
3430 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3431 update_presets_list(tbl);
3432 }
3434 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3435 {
3436 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3437 update_presets_list(tbl);
3438 }
3440 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3441 {
3442 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3443 update_presets_list(tbl);
3444 }
3446 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3447 {
3448 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3449 update_presets_list(tbl);
3450 }
3452 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3453 {
3454 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3455 update_presets_list(tbl);
3456 }
3458 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3459 {
3460 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3461 update_presets_list(tbl);
3462 }
3464 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3465 {
3466 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3467 update_presets_list(tbl);
3468 }
3470 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
3471 {
3472 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3473 update_presets_list(tbl);
3474 }
3476 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
3477 {
3478 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3479 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3480 update_presets_list(tbl);
3481 if (calligraphy_angle )
3482 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3483 }
3486 #define PROFILE_FLOAT_SIZE 7
3487 #define PROFILE_INT_SIZE 4
3488 struct ProfileFloatElement {
3489 char const *name;
3490 double def;
3491 double min;
3492 double max;
3493 };
3494 struct ProfileIntElement {
3495 char const *name;
3496 int def;
3497 int min;
3498 int max;
3499 };
3503 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3504 {"mass",0.02, 0.0, 1.0},
3505 {"wiggle",0.0, 0.0, 1.0},
3506 {"angle",30.0, -90.0, 90.0},
3507 {"thinning",0.1, -1.0, 1.0},
3508 {"tremor",0.0, 0.0, 1.0},
3509 {"flatness",0.9, 0.0, 1.0},
3510 {"cap_rounding",0.0, 0.0, 5.0}
3511 };
3512 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3513 {"width",15, 1, 100},
3514 {"usepressure",1,0,1},
3515 {"tracebackground",0,0,1},
3516 {"usetilt",1,0,1},
3517 };
3521 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3522 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3523 if (! desktop) return;
3525 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3526 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3527 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3529 unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3530 gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3531 gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);
3533 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3534 ProfileFloatElement const &pe = f_profile[i];
3535 double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3536 prefs_set_double_attribute(pref_path,pe.name,v);
3537 }
3538 for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3539 ProfileIntElement const &pe = i_profile[i];
3540 int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3541 prefs_set_int_attribute(pref_path,pe.name,v);
3542 }
3543 prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3545 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3546 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3547 GtkTreeIter iter;
3548 gtk_list_store_append( model, &iter );
3549 gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3551 free(profile_id);
3552 free(pref_path);
3554 ege_select_one_action_set_active(selector, new_index);
3555 }
3558 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3560 gint preset_index = ege_select_one_action_get_active( act );
3561 gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3563 if ( profile_name) {
3564 g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3565 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3566 ProfileFloatElement const &pe = f_profile[i];
3567 double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3568 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3569 if ( adj ) {
3570 gtk_adjustment_set_value(adj, v);
3571 }
3572 }
3573 for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3574 ProfileIntElement const &pe = i_profile[i];
3575 int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3576 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3577 if ( toggle ) {
3578 gtk_toggle_action_set_active(toggle, v);
3579 } else printf("No toggle");
3580 }
3581 free(profile_name);
3582 g_object_set_data(dataKludge, "profile_selector",act); //restore selector visibility
3583 }
3585 }
3588 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3589 {
3590 {
3591 EgeAdjustmentAction* calligraphy_angle = 0;
3593 {
3594 /* Width */
3595 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3596 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3597 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3598 _("Pen Width"), _("Width:"),
3599 _("The width of the calligraphic pen (relative to the visible canvas area)"),
3600 "tools.calligraphic", "width", 15,
3601 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3602 1, 100, 1.0, 10.0,
3603 labels, values, G_N_ELEMENTS(labels),
3604 sp_ddc_width_value_changed, 0.01, 0, 100 );
3605 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3606 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3607 }
3609 {
3610 /* Thinning */
3611 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3612 gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3613 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3614 _("Stroke Thinning"), _("Thinning:"),
3615 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3616 "tools.calligraphic", "thinning", 0.1,
3617 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3618 -1.0, 1.0, 0.01, 0.1,
3619 labels, values, G_N_ELEMENTS(labels),
3620 sp_ddc_velthin_value_changed, 0.01, 2);
3621 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3622 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3623 }
3625 {
3626 /* Angle */
3627 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3628 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3629 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3630 _("Pen Angle"), _("Angle:"),
3631 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3632 "tools.calligraphic", "angle", 30,
3633 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3634 -90.0, 90.0, 1.0, 10.0,
3635 labels, values, G_N_ELEMENTS(labels),
3636 sp_ddc_angle_value_changed, 1, 0 );
3637 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3638 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3639 g_object_set_data( holder, "angle", eact );
3640 calligraphy_angle = eact;
3641 }
3643 {
3644 /* Fixation */
3645 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3646 gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3647 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3648 _("Fixation"), _("Fixation:"),
3649 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3650 "tools.calligraphic", "flatness", 0.9,
3651 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3652 0.0, 1.0, 0.01, 0.1,
3653 labels, values, G_N_ELEMENTS(labels),
3654 sp_ddc_flatness_value_changed, 0.01, 2 );
3655 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3656 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3657 }
3659 {
3660 /* Cap Rounding */
3661 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3662 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3663 // TRANSLATORS: "cap" means "end" (both start and finish) here
3664 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3665 _("Cap rounding"), _("Caps:"),
3666 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3667 "tools.calligraphic", "cap_rounding", 0.0,
3668 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3669 0.0, 5.0, 0.01, 0.1,
3670 labels, values, G_N_ELEMENTS(labels),
3671 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3672 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3673 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3674 }
3676 {
3677 /* Tremor */
3678 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3679 gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3680 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3681 _("Stroke Tremor"), _("Tremor:"),
3682 _("Increase to make strokes rugged and trembling"),
3683 "tools.calligraphic", "tremor", 0.0,
3684 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3685 0.0, 1.0, 0.01, 0.1,
3686 labels, values, G_N_ELEMENTS(labels),
3687 sp_ddc_tremor_value_changed, 0.01, 2 );
3689 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3690 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3691 }
3693 {
3694 /* Wiggle */
3695 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3696 gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3697 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3698 _("Pen Wiggle"), _("Wiggle:"),
3699 _("Increase to make the pen waver and wiggle"),
3700 "tools.calligraphic", "wiggle", 0.0,
3701 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3702 0.0, 1.0, 0.01, 0.1,
3703 labels, values, G_N_ELEMENTS(labels),
3704 sp_ddc_wiggle_value_changed, 0.01, 2 );
3705 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3706 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3707 }
3709 {
3710 /* Mass */
3711 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3712 gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3713 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3714 _("Pen Mass"), _("Mass:"),
3715 _("Increase to make the pen drag behind, as if slowed by inertia"),
3716 "tools.calligraphic", "mass", 0.02,
3717 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3718 0.0, 1.0, 0.01, 0.1,
3719 labels, values, G_N_ELEMENTS(labels),
3720 sp_ddc_mass_value_changed, 0.01, 2 );
3721 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3722 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3723 }
3726 /* Trace Background button */
3727 {
3728 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3729 _("Trace Background"),
3730 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3731 "trace_background",
3732 Inkscape::ICON_SIZE_DECORATION );
3733 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3734 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
3735 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3736 g_object_set_data( holder, "tracebackground", act );
3737 }
3739 /* Use Pressure button */
3740 {
3741 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3742 _("Pressure"),
3743 _("Use the pressure of the input device to alter the width of the pen"),
3744 "use_pressure",
3745 Inkscape::ICON_SIZE_DECORATION );
3746 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3747 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
3748 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3749 g_object_set_data( holder, "usepressure", act );
3750 }
3752 /* Use Tilt button */
3753 {
3754 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3755 _("Tilt"),
3756 _("Use the tilt of the input device to alter the angle of the pen's nib"),
3757 "use_tilt",
3758 Inkscape::ICON_SIZE_DECORATION );
3759 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3760 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
3761 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3762 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3763 g_object_set_data( holder, "usetilt", act );
3764 }
3766 /*calligraphic profile */
3767 {
3768 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3769 gchar *pref_path;
3772 GtkTreeIter iter;
3773 gtk_list_store_append( model, &iter );
3774 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3776 //TODO: switch back to prefs API
3777 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3778 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3779 int ii=1;
3780 while (child_repr) {
3781 GtkTreeIter iter;
3782 char *preset_name = (char *) child_repr->attribute("name");
3783 gtk_list_store_append( model, &iter );
3784 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
3785 child_repr = sp_repr_next(child_repr);
3786 }
3788 pref_path = NULL;
3789 EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model) );
3790 ege_select_one_action_set_appearance( act1, "compact" );
3791 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
3792 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3793 g_object_set_data( holder, "profile_selector", act1 );
3795 }
3797 /*Save or delete calligraphic profile */
3798 {
3799 GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
3800 _("Defaults"),
3801 _("Save current settings as new profile"),
3802 GTK_STOCK_SAVE );
3803 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
3806 gtk_action_group_add_action( mainActions, act );
3807 gtk_action_set_sensitive( act, TRUE );
3808 g_object_set_data( holder, "profile_save_delete", act );
3809 }
3810 }
3811 }
3814 //########################
3815 //## Circle / Arc ##
3816 //########################
3818 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3819 {
3820 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3821 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3823 if (v1 == 0 && v2 == 0) {
3824 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3825 gtk_action_set_sensitive( ocb, FALSE );
3826 gtk_action_set_sensitive( make_whole, FALSE );
3827 }
3828 } else {
3829 gtk_action_set_sensitive( ocb, TRUE );
3830 gtk_action_set_sensitive( make_whole, TRUE );
3831 }
3832 }
3834 static void
3835 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3836 {
3837 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3839 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3840 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3841 }
3843 // quit if run by the attr_changed listener
3844 if (g_object_get_data( tbl, "freeze" )) {
3845 return;
3846 }
3848 // in turn, prevent listener from responding
3849 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3851 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3853 bool modmade = false;
3854 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3855 items != NULL;
3856 items = items->next)
3857 {
3858 SPItem *item = SP_ITEM(items->data);
3860 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3862 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3863 SPArc *arc = SP_ARC(item);
3865 if (!strcmp(value_name, "start"))
3866 ge->start = (adj->value * M_PI)/ 180;
3867 else
3868 ge->end = (adj->value * M_PI)/ 180;
3870 sp_genericellipse_normalize(ge);
3871 ((SPObject *)arc)->updateRepr();
3872 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3874 modmade = true;
3875 }
3876 }
3878 g_free(namespaced_name);
3880 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3882 sp_arctb_sensitivize( tbl, adj->value, other->value );
3884 if (modmade) {
3885 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3886 _("Arc: Change start/end"));
3887 }
3889 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3890 }
3893 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
3894 {
3895 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
3896 }
3898 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3899 {
3900 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
3901 }
3904 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3905 {
3906 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3907 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
3908 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3909 prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
3910 }
3912 // only take action if run by the attr_changed listener
3913 if (!g_object_get_data( tbl, "freeze" )) {
3914 // in turn, prevent listener from responding
3915 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3917 if ( eraserMode != 0 ) {
3918 } else {
3919 }
3920 // TODO finish implementation
3922 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3923 }
3924 }
3926 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3927 {
3928 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3929 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3930 if ( ege_select_one_action_get_active( act ) != 0 ) {
3931 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3932 } else {
3933 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3934 }
3935 }
3937 // quit if run by the attr_changed listener
3938 if (g_object_get_data( tbl, "freeze" )) {
3939 return;
3940 }
3942 // in turn, prevent listener from responding
3943 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3945 bool modmade = false;
3947 if ( ege_select_one_action_get_active(act) != 0 ) {
3948 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3949 items != NULL;
3950 items = items->next)
3951 {
3952 if (SP_IS_ARC((SPItem *) items->data)) {
3953 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3954 repr->setAttribute("sodipodi:open", "true");
3955 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3956 modmade = true;
3957 }
3958 }
3959 } else {
3960 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3961 items != NULL;
3962 items = items->next)
3963 {
3964 if (SP_IS_ARC((SPItem *) items->data)) {
3965 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3966 repr->setAttribute("sodipodi:open", NULL);
3967 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3968 modmade = true;
3969 }
3970 }
3971 }
3973 if (modmade) {
3974 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3975 _("Arc: Change open/closed"));
3976 }
3978 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3979 }
3981 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3982 {
3983 GtkAdjustment *adj;
3984 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3985 gtk_adjustment_set_value(adj, 0.0);
3986 gtk_adjustment_value_changed(adj);
3988 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3989 gtk_adjustment_set_value(adj, 0.0);
3990 gtk_adjustment_value_changed(adj);
3992 spinbutton_defocus( GTK_OBJECT(obj) );
3993 }
3995 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3996 gchar const */*old_value*/, gchar const */*new_value*/,
3997 bool /*is_interactive*/, gpointer data)
3998 {
3999 GObject *tbl = G_OBJECT(data);
4001 // quit if run by the _changed callbacks
4002 if (g_object_get_data( tbl, "freeze" )) {
4003 return;
4004 }
4006 // in turn, prevent callbacks from responding
4007 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4009 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4010 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4012 GtkAdjustment *adj1,*adj2;
4013 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4014 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4015 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4016 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4018 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4020 char const *openstr = NULL;
4021 openstr = repr->attribute("sodipodi:open");
4022 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4024 if (openstr) {
4025 ege_select_one_action_set_active( ocb, 1 );
4026 } else {
4027 ege_select_one_action_set_active( ocb, 0 );
4028 }
4030 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4031 }
4033 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4034 NULL, /* child_added */
4035 NULL, /* child_removed */
4036 arc_tb_event_attr_changed,
4037 NULL, /* content_changed */
4038 NULL /* order_changed */
4039 };
4042 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4043 {
4044 int n_selected = 0;
4045 Inkscape::XML::Node *repr = NULL;
4047 purge_repr_listener( tbl, tbl );
4049 for (GSList const *items = selection->itemList();
4050 items != NULL;
4051 items = items->next)
4052 {
4053 if (SP_IS_ARC((SPItem *) items->data)) {
4054 n_selected++;
4055 repr = SP_OBJECT_REPR((SPItem *) items->data);
4056 }
4057 }
4059 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4061 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4062 if (n_selected == 0) {
4063 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4064 } else if (n_selected == 1) {
4065 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4066 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4068 if (repr) {
4069 g_object_set_data( tbl, "repr", repr );
4070 Inkscape::GC::anchor(repr);
4071 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4072 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4073 }
4074 } else {
4075 // FIXME: implement averaging of all parameters for multiple selected
4076 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4077 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4078 sp_arctb_sensitivize( tbl, 1, 0 );
4079 }
4080 }
4083 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4084 {
4085 EgeAdjustmentAction* eact = 0;
4086 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4089 {
4090 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4091 ege_output_action_set_use_markup( act, TRUE );
4092 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4093 g_object_set_data( holder, "mode_action", act );
4094 }
4096 /* Start */
4097 {
4098 eact = create_adjustment_action( "ArcStartAction",
4099 _("Start"), _("Start:"),
4100 _("The angle (in degrees) from the horizontal to the arc's start point"),
4101 "tools.shapes.arc", "start", 0.0,
4102 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4103 -360.0, 360.0, 1.0, 10.0,
4104 0, 0, 0,
4105 sp_arctb_start_value_changed);
4106 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4107 }
4109 /* End */
4110 {
4111 eact = create_adjustment_action( "ArcEndAction",
4112 _("End"), _("End:"),
4113 _("The angle (in degrees) from the horizontal to the arc's end point"),
4114 "tools.shapes.arc", "end", 0.0,
4115 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4116 -360.0, 360.0, 1.0, 10.0,
4117 0, 0, 0,
4118 sp_arctb_end_value_changed);
4119 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4120 }
4122 /* Segments / Pie checkbox */
4123 {
4124 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4126 GtkTreeIter iter;
4127 gtk_list_store_append( model, &iter );
4128 gtk_list_store_set( model, &iter,
4129 0, _("Closed arc"),
4130 1, _("Switch to segment (closed shape with two radii)"),
4131 2, "circle_closed_arc",
4132 -1 );
4134 gtk_list_store_append( model, &iter );
4135 gtk_list_store_set( model, &iter,
4136 0, _("Open Arc"),
4137 1, _("Switch to arc (unclosed shape)"),
4138 2, "circle_open_arc",
4139 -1 );
4141 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4142 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4143 g_object_set_data( holder, "open_action", act );
4145 ege_select_one_action_set_appearance( act, "full" );
4146 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4147 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4148 ege_select_one_action_set_icon_column( act, 2 );
4149 ege_select_one_action_set_icon_size( act, secondarySize );
4150 ege_select_one_action_set_tooltip_column( act, 1 );
4152 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4153 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4154 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4155 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4156 }
4158 /* Make Whole */
4159 {
4160 InkAction* inky = ink_action_new( "ArcResetAction",
4161 _("Make whole"),
4162 _("Make the shape a whole ellipse, not arc or segment"),
4163 "reset_circle",
4164 secondarySize );
4165 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4166 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4167 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4168 g_object_set_data( holder, "make_whole", inky );
4169 }
4171 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4172 // sensitivize make whole and open checkbox
4173 {
4174 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4175 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4176 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4177 }
4180 sigc::connection *connection = new sigc::connection(
4181 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4182 );
4183 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4184 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4185 }
4190 // toggle button callbacks and updaters
4192 //########################
4193 //## Dropper ##
4194 //########################
4196 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4197 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4198 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4199 if ( set_action ) {
4200 if ( gtk_toggle_action_get_active( act ) ) {
4201 gtk_action_set_sensitive( set_action, TRUE );
4202 } else {
4203 gtk_action_set_sensitive( set_action, FALSE );
4204 }
4205 }
4207 spinbutton_defocus(GTK_OBJECT(tbl));
4208 }
4210 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4211 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4212 spinbutton_defocus(GTK_OBJECT(tbl));
4213 }
4216 /**
4217 * Dropper auxiliary toolbar construction and setup.
4218 *
4219 * TODO: Would like to add swatch of current color.
4220 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4221 * can drag and drop places. Will provide a nice mixing palette.
4222 */
4223 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4224 {
4225 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4227 {
4228 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4229 ege_output_action_set_use_markup( act, TRUE );
4230 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4231 }
4233 {
4234 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4235 _("Pick opacity"),
4236 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4237 NULL,
4238 Inkscape::ICON_SIZE_DECORATION );
4239 g_object_set( act, "short_label", _("Pick"), NULL );
4240 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4241 g_object_set_data( holder, "pick_action", act );
4242 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4243 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4244 }
4246 {
4247 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4248 _("Assign opacity"),
4249 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4250 NULL,
4251 Inkscape::ICON_SIZE_DECORATION );
4252 g_object_set( act, "short_label", _("Assign"), NULL );
4253 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4254 g_object_set_data( holder, "set_action", act );
4255 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4256 // make sure it's disabled if we're not picking alpha
4257 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4258 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4259 }
4260 }
4264 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4265 {
4266 {
4267 /* Width */
4268 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4269 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4270 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4271 _("Pen Width"), _("Width:"),
4272 _("The width of the eraser pen (relative to the visible canvas area)"),
4273 "tools.eraser", "width", 15,
4274 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4275 1, 100, 1.0, 10.0,
4276 labels, values, G_N_ELEMENTS(labels),
4277 sp_ddc_width_value_changed, 0.01, 0, 100 );
4278 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4279 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4280 }
4282 {
4283 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4285 GtkTreeIter iter;
4286 gtk_list_store_append( model, &iter );
4287 gtk_list_store_set( model, &iter,
4288 0, _("Delete"),
4289 1, _("Delete objects touched by the eraser"),
4290 2, "delete_object",
4291 -1 );
4293 gtk_list_store_append( model, &iter );
4294 gtk_list_store_set( model, &iter,
4295 0, _("Cut"),
4296 1, _("Cut out from objects"),
4297 2, "difference",
4298 -1 );
4300 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4301 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4302 g_object_set_data( holder, "eraser_mode_action", act );
4304 ege_select_one_action_set_appearance( act, "full" );
4305 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4306 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4307 ege_select_one_action_set_icon_column( act, 2 );
4308 ege_select_one_action_set_tooltip_column( act, 1 );
4310 gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4311 ege_select_one_action_set_active( act, eraserMode );
4312 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4313 }
4315 }
4317 //########################
4318 //## Text Toolbox ##
4319 //########################
4320 /*
4321 static void
4322 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4323 {
4324 //Call back for letter sizing spinbutton
4325 }
4327 static void
4328 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4329 {
4330 //Call back for line height spinbutton
4331 }
4333 static void
4334 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4335 {
4336 //Call back for horizontal kerning spinbutton
4337 }
4339 static void
4340 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4341 {
4342 //Call back for vertical kerning spinbutton
4343 }
4345 static void
4346 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4347 {
4348 //Call back for letter rotation spinbutton
4349 }*/
4351 namespace {
4353 bool popdown_visible = false;
4354 bool popdown_hasfocus = false;
4356 void
4357 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4358 {
4359 SPStyle *query =
4360 sp_style_new (SP_ACTIVE_DOCUMENT);
4362 int result_fontspec =
4363 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4365 int result_family =
4366 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4368 int result_style =
4369 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4371 int result_numbers =
4372 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4374 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4376 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4377 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4378 {
4379 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4381 if (repr)
4382 {
4383 sp_style_read_from_repr (query, repr);
4384 }
4385 else
4386 {
4387 return;
4388 }
4389 }
4391 if (query->text)
4392 {
4393 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4394 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4395 gtk_entry_set_text (GTK_ENTRY (entry), "");
4397 } else if (query->text->font_specification.value || query->text->font_family.value) {
4399 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4401 // Get the font that corresponds
4402 Glib::ustring familyName;
4404 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4405 if (font) {
4406 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4407 font->Unref();
4408 font = NULL;
4409 }
4411 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4413 Gtk::TreePath path;
4414 try {
4415 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4416 } catch (...) {
4417 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4418 return;
4419 }
4421 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4422 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4424 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4426 gtk_tree_selection_select_path (tselection, path.gobj());
4427 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4429 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4430 }
4432 //Size
4433 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4434 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4435 g_object_set_data (tbl, "size-block", gpointer(1));
4436 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4437 g_object_set_data (tbl, "size-block", gpointer(0));
4438 free (str);
4440 //Anchor
4441 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4442 {
4443 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4444 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4445 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4446 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4447 }
4448 else
4449 {
4450 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4451 {
4452 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4453 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4454 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4455 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4456 }
4457 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4458 {
4459 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4460 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4461 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4462 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4463 }
4464 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4465 {
4466 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4467 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4468 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4469 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4470 }
4471 }
4473 //Style
4474 {
4475 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4477 gboolean active = gtk_toggle_button_get_active (button);
4478 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4480 if (active != check)
4481 {
4482 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4483 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4484 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4485 }
4486 }
4488 {
4489 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4491 gboolean active = gtk_toggle_button_get_active (button);
4492 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4494 if (active != check)
4495 {
4496 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4497 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4498 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4499 }
4500 }
4502 //Orientation
4503 //locking both buttons, changing one affect all group (both)
4504 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4505 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4507 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4508 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4510 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4511 {
4512 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4513 }
4514 else
4515 {
4516 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4517 }
4518 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4519 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4520 }
4522 sp_style_unref(query);
4523 }
4525 void
4526 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4527 {
4528 sp_text_toolbox_selection_changed (selection, tbl);
4529 }
4531 void
4532 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4533 {
4534 sp_text_toolbox_selection_changed (NULL, tbl);
4535 }
4537 void
4538 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
4539 GObject *tbl)
4540 {
4541 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4542 GtkTreeModel *model = 0;
4543 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4544 GtkTreeIter iter;
4545 char *family = 0;
4547 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4548 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4550 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4551 return;
4552 }
4554 gtk_tree_model_get (model, &iter, 0, &family, -1);
4556 if (g_object_get_data (G_OBJECT (selection), "block"))
4557 {
4558 gtk_entry_set_text (GTK_ENTRY (entry), family);
4559 return;
4560 }
4562 gtk_entry_set_text (GTK_ENTRY (entry), family);
4564 SPStyle *query =
4565 sp_style_new (SP_ACTIVE_DOCUMENT);
4567 int result_fontspec =
4568 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4570 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4572 SPCSSAttr *css = sp_repr_css_attr_new ();
4575 // First try to get the font spec from the stored value
4576 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4578 if (fontSpec.empty()) {
4579 // Construct a new font specification if it does not yet exist
4580 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4581 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4582 fontFromStyle->Unref();
4583 }
4585 if (!fontSpec.empty()) {
4586 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4587 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4588 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4589 if (font) {
4590 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4592 // Set all the these just in case they were altered when finding the best
4593 // match for the new family and old style...
4595 gchar c[256];
4597 font->Family(c, 256);
4598 sp_repr_css_set_property (css, "font-family", c);
4600 font->Attribute( "weight", c, 256);
4601 sp_repr_css_set_property (css, "font-weight", c);
4603 font->Attribute("style", c, 256);
4604 sp_repr_css_set_property (css, "font-style", c);
4606 font->Attribute("stretch", c, 256);
4607 sp_repr_css_set_property (css, "font-stretch", c);
4609 font->Attribute("variant", c, 256);
4610 sp_repr_css_set_property (css, "font-variant", c);
4612 font->Unref();
4613 }
4614 }
4615 }
4617 // If querying returned nothing, set the default style of the tool (for new texts)
4618 if (result_fontspec == QUERY_STYLE_NOTHING)
4619 {
4620 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4621 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4622 }
4623 else
4624 {
4625 sp_desktop_set_style (desktop, css, true, true);
4626 }
4628 sp_style_unref(query);
4630 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4631 _("Text: Change font family"));
4632 sp_repr_css_attr_unref (css);
4633 free (family);
4634 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4636 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4637 }
4639 void
4640 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
4641 GObject *tbl)
4642 {
4643 const char *family = gtk_entry_get_text (entry);
4645 try {
4646 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4647 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4648 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4649 gtk_tree_selection_select_path (selection, path.gobj());
4650 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4651 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4652 } catch (...) {
4653 if (family && strlen (family))
4654 {
4655 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4656 }
4657 }
4658 }
4660 void
4661 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
4662 gpointer data)
4663 {
4664 if (g_object_get_data (G_OBJECT (button), "block")) return;
4665 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4666 int prop = GPOINTER_TO_INT(data);
4668 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4669 SPCSSAttr *css = sp_repr_css_attr_new ();
4671 switch (prop)
4672 {
4673 case 0:
4674 {
4675 sp_repr_css_set_property (css, "text-anchor", "start");
4676 sp_repr_css_set_property (css, "text-align", "start");
4677 break;
4678 }
4679 case 1:
4680 {
4681 sp_repr_css_set_property (css, "text-anchor", "middle");
4682 sp_repr_css_set_property (css, "text-align", "center");
4683 break;
4684 }
4686 case 2:
4687 {
4688 sp_repr_css_set_property (css, "text-anchor", "end");
4689 sp_repr_css_set_property (css, "text-align", "end");
4690 break;
4691 }
4693 case 3:
4694 {
4695 sp_repr_css_set_property (css, "text-anchor", "start");
4696 sp_repr_css_set_property (css, "text-align", "justify");
4697 break;
4698 }
4699 }
4701 SPStyle *query =
4702 sp_style_new (SP_ACTIVE_DOCUMENT);
4703 int result_numbers =
4704 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4706 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4707 if (result_numbers == QUERY_STYLE_NOTHING)
4708 {
4709 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4710 }
4712 sp_style_unref(query);
4714 sp_desktop_set_style (desktop, css, true, true);
4715 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4716 _("Text: Change alignment"));
4717 sp_repr_css_attr_unref (css);
4719 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4720 }
4722 void
4723 sp_text_toolbox_style_toggled (GtkToggleButton *button,
4724 gpointer data)
4725 {
4726 if (g_object_get_data (G_OBJECT (button), "block")) return;
4728 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4729 SPCSSAttr *css = sp_repr_css_attr_new ();
4730 int prop = GPOINTER_TO_INT(data);
4731 bool active = gtk_toggle_button_get_active (button);
4733 SPStyle *query =
4734 sp_style_new (SP_ACTIVE_DOCUMENT);
4736 int result_fontspec =
4737 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4739 int result_family =
4740 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4742 int result_style =
4743 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4745 int result_numbers =
4746 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4748 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4749 Glib::ustring newFontSpec = "";
4751 if (fontSpec.empty()) {
4752 // Construct a new font specification if it does not yet exist
4753 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4754 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4755 fontFromStyle->Unref();
4756 }
4758 switch (prop)
4759 {
4760 case 0:
4761 {
4762 if (!fontSpec.empty()) {
4763 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4764 }
4765 if (fontSpec != newFontSpec) {
4766 // Don't even set the bold if the font didn't exist on the system
4767 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4768 }
4769 break;
4770 }
4772 case 1:
4773 {
4774 if (!fontSpec.empty()) {
4775 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4776 }
4777 if (fontSpec != newFontSpec) {
4778 // Don't even set the italic if the font didn't exist on the system
4779 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4780 }
4781 break;
4782 }
4783 }
4785 if (!newFontSpec.empty()) {
4786 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4787 }
4789 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4790 if (result_fontspec == QUERY_STYLE_NOTHING)
4791 {
4792 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4793 }
4795 sp_style_unref(query);
4797 sp_desktop_set_style (desktop, css, true, true);
4798 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4799 _("Text: Change font style"));
4800 sp_repr_css_attr_unref (css);
4802 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4803 }
4805 void
4806 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
4807 gpointer data)
4808 {
4809 if (g_object_get_data (G_OBJECT (button), "block")) {
4810 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4811 return;
4812 }
4814 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4815 SPCSSAttr *css = sp_repr_css_attr_new ();
4816 int prop = GPOINTER_TO_INT(data);
4818 switch (prop)
4819 {
4820 case 0:
4821 {
4822 sp_repr_css_set_property (css, "writing-mode", "lr");
4823 break;
4824 }
4826 case 1:
4827 {
4828 sp_repr_css_set_property (css, "writing-mode", "tb");
4829 break;
4830 }
4831 }
4833 SPStyle *query =
4834 sp_style_new (SP_ACTIVE_DOCUMENT);
4835 int result_numbers =
4836 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4838 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4839 if (result_numbers == QUERY_STYLE_NOTHING)
4840 {
4841 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4842 }
4844 sp_desktop_set_style (desktop, css, true, true);
4845 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4846 _("Text: Change orientation"));
4847 sp_repr_css_attr_unref (css);
4849 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4850 }
4852 gboolean
4853 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4854 {
4855 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4856 if (!desktop) return FALSE;
4858 switch (get_group0_keyval (event)) {
4859 case GDK_Escape: // defocus
4860 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4861 sp_text_toolbox_selection_changed (NULL, tbl); // update
4862 return TRUE; // I consumed the event
4863 break;
4864 }
4865 return FALSE;
4866 }
4868 gboolean
4869 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4870 {
4871 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4872 if (!desktop) return FALSE;
4874 switch (get_group0_keyval (event)) {
4875 case GDK_KP_Enter:
4876 case GDK_Return:
4877 case GDK_Escape: // defocus
4878 gtk_widget_hide (w);
4879 popdown_visible = false;
4880 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4881 return TRUE; // I consumed the event
4882 break;
4883 case GDK_w:
4884 case GDK_W:
4885 if (event->state & GDK_CONTROL_MASK) {
4886 gtk_widget_hide (w);
4887 popdown_visible = false;
4888 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4889 return TRUE; // I consumed the event
4890 }
4891 break;
4892 }
4893 return FALSE;
4894 }
4897 void
4898 sp_text_toolbox_size_changed (GtkComboBox *cbox,
4899 GObject *tbl)
4900 {
4901 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4903 if (g_object_get_data (tbl, "size-block")) return;
4905 // If this is not from selecting a size in the list (in which case get_active will give the
4906 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4907 // process this event. This fixes GTK's stupid insistence on sending an activate change every
4908 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4909 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4910 return;
4912 gchar *endptr;
4913 gdouble value = -1;
4914 char *text = gtk_combo_box_get_active_text (cbox);
4915 if (text) {
4916 value = g_strtod (text, &endptr);
4917 if (endptr == text) // conversion failed, non-numeric input
4918 value = -1;
4919 free (text);
4920 }
4921 if (value <= 0) {
4922 return; // could not parse value
4923 }
4925 SPCSSAttr *css = sp_repr_css_attr_new ();
4926 Inkscape::CSSOStringStream osfs;
4927 osfs << value;
4928 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4930 SPStyle *query =
4931 sp_style_new (SP_ACTIVE_DOCUMENT);
4932 int result_numbers =
4933 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4935 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4936 if (result_numbers == QUERY_STYLE_NOTHING)
4937 {
4938 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4939 }
4941 sp_style_unref(query);
4943 sp_desktop_set_style (desktop, css, true, true);
4944 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4945 _("Text: Change font size"));
4946 sp_repr_css_attr_unref (css);
4948 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4949 }
4951 gboolean
4952 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
4953 {
4954 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4955 if (!desktop) return FALSE;
4957 if (!g_object_get_data (tbl, "esc-pressed")) {
4958 g_object_set_data (tbl, "enter-pressed", gpointer(1));
4959 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4960 sp_text_toolbox_size_changed (cbox, tbl);
4961 g_object_set_data (tbl, "enter-pressed", gpointer(0));
4962 }
4963 return FALSE; // I consumed the event
4964 }
4967 gboolean
4968 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4969 {
4970 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4971 if (!desktop) return FALSE;
4973 switch (get_group0_keyval (event)) {
4974 case GDK_Escape: // defocus
4975 g_object_set_data (tbl, "esc-pressed", gpointer(1));
4976 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4977 g_object_set_data (tbl, "esc-pressed", gpointer(0));
4978 return TRUE; // I consumed the event
4979 break;
4980 case GDK_Return: // defocus
4981 case GDK_KP_Enter:
4982 g_object_set_data (tbl, "enter-pressed", gpointer(1));
4983 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4984 sp_text_toolbox_size_changed (cbox, tbl);
4985 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4986 g_object_set_data (tbl, "enter-pressed", gpointer(0));
4987 return TRUE; // I consumed the event
4988 break;
4989 }
4990 return FALSE;
4991 }
4993 void
4994 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
4995 GObject *tbl)
4996 {
4997 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4998 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4999 int x, y;
5001 if (!popdown_visible)
5002 {
5003 gdk_window_get_origin (widget->window, &x, &y);
5004 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5005 gtk_widget_show_all (popdown);
5006 //sp_transientize (popdown);
5008 gdk_pointer_grab (widget->window, TRUE,
5009 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5010 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5011 GDK_POINTER_MOTION_MASK),
5012 NULL, NULL, GDK_CURRENT_TIME);
5014 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5016 popdown_visible = true;
5017 }
5018 else
5019 {
5020 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5021 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5022 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5023 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5024 gtk_widget_hide (popdown);
5025 popdown_visible = false;
5026 }
5027 }
5029 gboolean
5030 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
5031 GdkEventFocus */*event*/,
5032 GObject */*tbl*/)
5033 {
5034 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5035 return FALSE;
5036 }
5038 gboolean
5039 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
5040 GdkEventFocus */*event*/,
5041 GObject */*tbl*/)
5042 {
5043 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5045 if (popdown_hasfocus) {
5046 gtk_widget_hide (popdown);
5047 popdown_hasfocus = false;
5048 popdown_visible = false;
5049 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5050 return TRUE;
5051 }
5052 return FALSE;
5053 }
5055 gboolean
5056 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
5057 GdkEventFocus */*event*/,
5058 GObject */*tbl*/)
5059 {
5060 popdown_hasfocus = true;
5061 return TRUE;
5062 }
5065 void
5066 cell_data_func (GtkTreeViewColumn */*column*/,
5067 GtkCellRenderer *cell,
5068 GtkTreeModel *tree_model,
5069 GtkTreeIter *iter,
5070 gpointer /*data*/)
5071 {
5072 char *family,
5073 *family_escaped,
5074 *sample_escaped;
5076 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5078 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5080 family_escaped = g_markup_escape_text (family, -1);
5081 sample_escaped = g_markup_escape_text (sample, -1);
5083 std::stringstream markup;
5084 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5085 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5087 free (family);
5088 free (family_escaped);
5089 free (sample_escaped);
5090 }
5092 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5093 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5094 if (completion) {
5095 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5096 g_object_unref (completion);
5097 }
5098 }
5100 GtkWidget*
5101 sp_text_toolbox_new (SPDesktop *desktop)
5102 {
5103 GtkWidget *tbl = gtk_hbox_new (FALSE, 0);
5104 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5106 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5107 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5109 GtkTooltips *tt = gtk_tooltips_new();
5110 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5112 ////////////Family
5113 //Window
5114 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5115 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5117 //Entry
5118 GtkWidget *entry = gtk_entry_new ();
5119 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5120 GtkEntryCompletion *completion = gtk_entry_completion_new ();
5121 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5122 gtk_entry_completion_set_text_column (completion, 0);
5123 gtk_entry_completion_set_minimum_key_length (completion, 1);
5124 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5125 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5126 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5127 aux_toolbox_space (tbl, 1);
5128 gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
5129 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5131 //Button
5132 GtkWidget *button = gtk_button_new ();
5133 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5134 gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, 0);
5136 //Popdown
5137 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
5138 GtkWidget *treeview = gtk_tree_view_new ();
5140 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5141 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
5142 gtk_tree_view_column_pack_start (column, cell, FALSE);
5143 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5144 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5145 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5147 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5148 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5149 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5151 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5153 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5154 gtk_container_add (GTK_CONTAINER (sw), treeview);
5156 gtk_container_add (GTK_CONTAINER (window), sw);
5157 gtk_widget_set_size_request (window, 300, 450);
5159 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5160 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5161 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5163 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5165 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5166 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5167 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5169 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5170 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5172 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5173 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5174 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5175 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5176 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5178 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5179 aux_toolbox_space (tbl, 1);
5180 GtkWidget *box = gtk_event_box_new ();
5181 gtk_container_add (GTK_CONTAINER (box), image);
5182 gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
5183 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5184 GtkTooltips *tooltips = gtk_tooltips_new ();
5185 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5186 gtk_widget_hide (GTK_WIDGET (box));
5187 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5189 ////////////Size
5190 const char *sizes[] = {
5191 "4", "6", "8", "9", "10", "11", "12", "13", "14",
5192 "16", "18", "20", "22", "24", "28",
5193 "32", "36", "40", "48", "56", "64", "72", "144"
5194 };
5196 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5197 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5198 gtk_widget_set_size_request (cbox, 80, -1);
5199 aux_toolbox_space (tbl, 1);
5200 gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
5201 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5202 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5203 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5204 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5206 //spacer
5207 aux_toolbox_space (tbl, 4);
5208 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5210 ////////////Text anchor
5211 GtkWidget *group = gtk_radio_button_new (NULL);
5212 GtkWidget *row = gtk_hbox_new (FALSE, 4);
5213 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5215 // left
5216 GtkWidget *rbutton = group;
5217 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5218 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5219 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5221 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5222 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
5223 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5224 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5226 // center
5227 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5228 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5229 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5230 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5232 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5233 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
5234 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5235 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5237 // right
5238 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5239 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5240 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5241 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5243 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5244 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5245 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5246 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5248 // fill
5249 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5250 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5251 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5252 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5254 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5255 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
5256 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5257 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5259 aux_toolbox_space (tbl, 1);
5260 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5262 //spacer
5263 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5265 ////////////Text style
5266 row = gtk_hbox_new (FALSE, 4);
5268 // bold
5269 rbutton = gtk_toggle_button_new ();
5270 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5271 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5272 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5273 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5275 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5276 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
5277 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5279 // italic
5280 rbutton = gtk_toggle_button_new ();
5281 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5282 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5283 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5284 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5286 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5287 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
5288 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5290 aux_toolbox_space (tbl, 1);
5291 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5293 //spacer
5294 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5296 ////////////Text orientation
5297 group = gtk_radio_button_new (NULL);
5298 row = gtk_hbox_new (FALSE, 4);
5299 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5301 // horizontal
5302 rbutton = group;
5303 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5304 gtk_container_add (GTK_CONTAINER (rbutton),
5305 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5306 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5307 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5309 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5310 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5311 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5313 // vertical
5314 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5315 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5316 gtk_container_add (GTK_CONTAINER (rbutton),
5317 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5318 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5319 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5321 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5322 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
5323 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5324 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5327 //watch selection
5328 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5330 sigc::connection *c_selection_changed =
5331 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5332 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5333 pool->add_connection ("selection-changed", c_selection_changed);
5335 sigc::connection *c_selection_modified =
5336 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5337 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5338 pool->add_connection ("selection-modified", c_selection_modified);
5340 sigc::connection *c_subselection_changed =
5341 new sigc::connection (desktop->connectToolSubselectionChanged
5342 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5343 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5345 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5348 gtk_widget_show_all (tbl);
5349 return tbl;
5351 } // end of sp_text_toolbox_new()
5353 }//<unnamed> namespace
5356 //#########################
5357 //## Connector ##
5358 //#########################
5360 static void sp_connector_path_set_avoid(void)
5361 {
5362 cc_selection_set_avoid(true);
5363 }
5366 static void sp_connector_path_set_ignore(void)
5367 {
5368 cc_selection_set_avoid(false);
5369 }
5373 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5374 {
5375 // quit if run by the _changed callbacks
5376 if (g_object_get_data( tbl, "freeze" )) {
5377 return;
5378 }
5380 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5381 SPDocument *doc = sp_desktop_document(desktop);
5383 if (!sp_document_get_undo_sensitive(doc))
5384 {
5385 return;
5386 }
5388 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5390 if ( repr->attribute("inkscape:connector-spacing") ) {
5391 gdouble priorValue = gtk_adjustment_get_value(adj);
5392 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5393 if ( priorValue == gtk_adjustment_get_value(adj) ) {
5394 return;
5395 }
5396 } else if ( adj->value == defaultConnSpacing ) {
5397 return;
5398 }
5400 // in turn, prevent callbacks from responding
5401 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5403 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5404 SP_OBJECT(desktop->namedview)->updateRepr();
5406 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5407 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5408 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5409 NR::Matrix m = NR::identity();
5410 avoid_item_move(&m, item);
5411 }
5413 if (items) {
5414 g_slist_free(items);
5415 }
5417 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5418 _("Change connector spacing"));
5420 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5422 spinbutton_defocus(GTK_OBJECT(tbl));
5423 }
5425 static void sp_connector_graph_layout(void)
5426 {
5427 if (!SP_ACTIVE_DESKTOP) return;
5429 // hack for clones, see comment in align-and-distribute.cpp
5430 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5431 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5433 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5435 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5437 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5438 }
5440 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5441 {
5442 if ( gtk_toggle_action_get_active( act ) ) {
5443 prefs_set_string_attribute("tools.connector", "directedlayout",
5444 "true");
5445 } else {
5446 prefs_set_string_attribute("tools.connector", "directedlayout",
5447 "false");
5448 }
5449 }
5451 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5452 {
5453 if ( gtk_toggle_action_get_active( act ) ) {
5454 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5455 "true");
5456 } else {
5457 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5458 "false");
5459 }
5460 }
5463 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5464 {
5465 prefs_set_double_attribute("tools.connector", "length", adj->value);
5466 spinbutton_defocus(GTK_OBJECT(tbl));
5467 }
5469 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5470 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5471 bool /*is_interactive*/, gpointer data)
5472 {
5473 GtkWidget *tbl = GTK_WIDGET(data);
5475 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5476 return;
5477 }
5478 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5479 return;
5480 }
5482 GtkAdjustment *adj = (GtkAdjustment*)
5483 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5484 gdouble spacing = defaultConnSpacing;
5485 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5487 gtk_adjustment_set_value(adj, spacing);
5488 }
5491 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5492 NULL, /* child_added */
5493 NULL, /* child_removed */
5494 connector_tb_event_attr_changed,
5495 NULL, /* content_changed */
5496 NULL /* order_changed */
5497 };
5500 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5501 {
5502 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5504 {
5505 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5506 _("Avoid"),
5507 _("Make connectors avoid selected objects"),
5508 "connector_avoid",
5509 secondarySize );
5510 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5511 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5512 }
5514 {
5515 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5516 _("Ignore"),
5517 _("Make connectors ignore selected objects"),
5518 "connector_ignore",
5519 secondarySize );
5520 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5521 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5522 }
5524 EgeAdjustmentAction* eact = 0;
5526 // Spacing spinbox
5527 eact = create_adjustment_action( "ConnectorSpacingAction",
5528 _("Connector Spacing"), _("Spacing:"),
5529 _("The amount of space left around objects by auto-routing connectors"),
5530 "tools.connector", "spacing", defaultConnSpacing,
5531 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5532 0, 100, 1.0, 10.0,
5533 0, 0, 0,
5534 connector_spacing_changed, 1, 0 );
5535 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5537 // Graph (connector network) layout
5538 {
5539 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5540 _("Graph"),
5541 _("Nicely arrange selected connector network"),
5542 "graph_layout",
5543 secondarySize );
5544 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5545 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5546 }
5548 // Default connector length spinbox
5549 eact = create_adjustment_action( "ConnectorLengthAction",
5550 _("Connector Length"), _("Length:"),
5551 _("Ideal length for connectors when layout is applied"),
5552 "tools.connector", "length", 100,
5553 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5554 10, 1000, 10.0, 100.0,
5555 0, 0, 0,
5556 connector_length_changed, 1, 0 );
5557 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5560 // Directed edges toggle button
5561 {
5562 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5563 _("Downwards"),
5564 _("Make connectors with end-markers (arrows) point downwards"),
5565 "directed_graph",
5566 Inkscape::ICON_SIZE_DECORATION );
5567 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5569 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5570 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5571 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5573 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5574 }
5576 // Avoid overlaps toggle button
5577 {
5578 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5579 _("Remove overlaps"),
5580 _("Do not allow overlapping shapes"),
5581 "remove_overlaps",
5582 Inkscape::ICON_SIZE_DECORATION );
5583 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5585 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5586 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5587 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5589 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5590 }
5592 // Code to watch for changes to the connector-spacing attribute in
5593 // the XML.
5594 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5595 g_assert(repr != NULL);
5597 purge_repr_listener( holder, holder );
5599 if (repr) {
5600 g_object_set_data( holder, "repr", repr );
5601 Inkscape::GC::anchor(repr);
5602 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5603 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5604 }
5605 } // end of sp_connector_toolbox_prep()
5608 //#########################
5609 //## Paintbucket ##
5610 //#########################
5612 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5613 {
5614 gint channels = ege_select_one_action_get_active( act );
5615 flood_channels_set_channels( channels );
5616 }
5618 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5619 {
5620 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5621 }
5623 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5624 {
5625 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5626 }
5628 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5629 {
5630 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5631 SPUnit const *unit = tracker->getActiveUnit();
5633 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5635 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5636 }
5638 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5639 {
5640 // FIXME: make defaults settable via Inkscape Options
5641 struct KeyValue {
5642 char const *key;
5643 double value;
5644 } const key_values[] = {
5645 {"threshold", 15},
5646 {"offset", 0.0}
5647 };
5649 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5650 KeyValue const &kv = key_values[i];
5651 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5652 if ( adj ) {
5653 gtk_adjustment_set_value(adj, kv.value);
5654 }
5655 }
5657 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5658 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5659 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5660 ege_select_one_action_set_active( autogap_action, 0 );
5661 }
5663 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5664 {
5665 EgeAdjustmentAction* eact = 0;
5667 {
5668 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5670 GList* items = 0;
5671 gint count = 0;
5672 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5673 {
5674 GtkTreeIter iter;
5675 gtk_list_store_append( model, &iter );
5676 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5677 count++;
5678 }
5679 g_list_free( items );
5680 items = 0;
5681 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5682 g_object_set( act1, "short_label", _("Fill by:"), NULL );
5683 ege_select_one_action_set_appearance( act1, "compact" );
5684 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5685 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5686 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5687 g_object_set_data( holder, "channels_action", act1 );
5688 }
5690 // Spacing spinbox
5691 {
5692 eact = create_adjustment_action(
5693 "ThresholdAction",
5694 _("Fill Threshold"), _("Threshold:"),
5695 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5696 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5697 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5698 0, 0, 0,
5699 paintbucket_threshold_changed, 1, 0 );
5701 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5702 }
5704 // Create the units menu.
5705 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5706 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5707 if (stored_unit)
5708 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5709 g_object_set_data( holder, "tracker", tracker );
5710 {
5711 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5712 gtk_action_group_add_action( mainActions, act );
5713 }
5715 // Offset spinbox
5716 {
5717 eact = create_adjustment_action(
5718 "OffsetAction",
5719 _("Grow/shrink by"), _("Grow/shrink by:"),
5720 _("The amount to grow (positive) or shrink (negative) the created fill path"),
5721 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5722 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5723 0, 0, 0,
5724 paintbucket_offset_changed, 1, 2);
5725 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5727 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5728 }
5730 /* Auto Gap */
5731 {
5732 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5734 GList* items = 0;
5735 gint count = 0;
5736 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5737 {
5738 GtkTreeIter iter;
5739 gtk_list_store_append( model, &iter );
5740 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5741 count++;
5742 }
5743 g_list_free( items );
5744 items = 0;
5745 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5746 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5747 ege_select_one_action_set_appearance( act2, "compact" );
5748 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5749 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5750 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5751 g_object_set_data( holder, "autogap_action", act2 );
5752 }
5754 /* Reset */
5755 {
5756 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5757 _("Defaults"),
5758 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5759 GTK_STOCK_CLEAR );
5760 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5761 gtk_action_group_add_action( mainActions, act );
5762 gtk_action_set_sensitive( act, TRUE );
5763 }
5765 }
5767 /*
5768 Local Variables:
5769 mode:c++
5770 c-file-style:"stroustrup"
5771 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5772 indent-tabs-mode:nil
5773 fill-column:99
5774 End:
5775 */
5776 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :