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