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