647d95d800f837004abab5fa6350137ab0ecc53e
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"
93 #include "interface.h"
94 #include "shortcuts.h"
96 #include "mod360.h"
98 #include "toolbox.h"
100 #include "flood-context.h"
102 #include "ink-action.h"
103 #include "ege-adjustment-action.h"
104 #include "ege-output-action.h"
105 #include "ege-select-one-action.h"
106 #include "helper/unit-tracker.h"
108 #include "svg/css-ostringstream.h"
110 #include "widgets/calligraphic-profile-rename.h"
112 using Inkscape::UnitTracker;
114 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
115 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
117 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
130 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
137 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
138 static Inkscape::IconSize sizeChoices[] = {
139 Inkscape::ICON_SIZE_LARGE_TOOLBAR,
140 Inkscape::ICON_SIZE_SMALL_TOOLBAR,
141 Inkscape::ICON_SIZE_MENU
142 };
143 int index = prefs_get_int_attribute_limited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
144 return sizeChoices[index];
145 }
147 static struct {
148 gchar const *type_name;
149 gchar const *data_name;
150 sp_verb_t verb;
151 sp_verb_t doubleclick_verb;
152 } const tools[] = {
153 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
154 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
155 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
156 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
157 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
158 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
159 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
160 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
161 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
162 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
163 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
164 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
165 { "SPEraserContext", "eraser_tool", SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
166 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
167 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
168 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
169 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
170 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
171 { NULL, NULL, 0, 0 }
172 };
174 static struct {
175 gchar const *type_name;
176 gchar const *data_name;
177 GtkWidget *(*create_func)(SPDesktop *desktop);
178 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
179 gchar const *ui_name;
180 gint swatch_verb_id;
181 gchar const *swatch_tool;
182 gchar const *swatch_tip;
183 } const aux_toolboxes[] = {
184 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
185 SP_VERB_INVALID, 0, 0},
186 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
187 SP_VERB_INVALID, 0, 0},
188 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
189 SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
190 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
191 SP_VERB_INVALID, 0, 0},
192 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
193 SP_VERB_CONTEXT_STAR_PREFS, "tools.shapes.star", N_("Style of new stars")},
194 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
195 SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", N_("Style of new rectangles")},
196 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
197 SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", N_("Style of new 3D boxes")},
198 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
199 SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", N_("Style of new ellipses")},
200 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
201 SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral", N_("Style of new spirals")},
202 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
203 SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
204 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
205 SP_VERB_CONTEXT_PEN_PREFS, "tools.freehand.pen", N_("Style of new paths created by Pen")},
206 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
207 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
208 { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
209 SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
210 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
211 SP_VERB_INVALID, 0, 0},
212 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
213 SP_VERB_INVALID, 0, 0},
214 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
215 SP_VERB_INVALID, 0, 0},
216 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
217 SP_VERB_INVALID, 0, 0},
218 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
219 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
220 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
221 };
224 static gchar const * ui_descr =
225 "<ui>"
226 " <toolbar name='SelectToolbar'>"
227 " <toolitem action='EditSelectAll' />"
228 " <toolitem action='EditSelectAllInAllLayers' />"
229 " <toolitem action='EditDeselect' />"
230 " <separator />"
231 " <toolitem action='ObjectRotate90CCW' />"
232 " <toolitem action='ObjectRotate90' />"
233 " <toolitem action='ObjectFlipHorizontally' />"
234 " <toolitem action='ObjectFlipVertically' />"
235 " <separator />"
236 " <toolitem action='SelectionToBack' />"
237 " <toolitem action='SelectionLower' />"
238 " <toolitem action='SelectionRaise' />"
239 " <toolitem action='SelectionToFront' />"
240 " <separator />"
241 " <toolitem action='XAction' />"
242 " <toolitem action='YAction' />"
243 " <toolitem action='WidthAction' />"
244 " <toolitem action='LockAction' />"
245 " <toolitem action='HeightAction' />"
246 " <toolitem action='UnitsAction' />"
247 " <separator />"
248 " <toolitem action='transform_affect_label' />"
249 " <toolitem action='transform_stroke' />"
250 " <toolitem action='transform_corners' />"
251 " <toolitem action='transform_gradient' />"
252 " <toolitem action='transform_pattern' />"
253 " </toolbar>"
255 " <toolbar name='NodeToolbar'>"
256 " <toolitem action='NodeInsertAction' />"
257 " <toolitem action='NodeDeleteAction' />"
258 " <separator />"
259 " <toolitem action='NodeJoinAction' />"
260 " <toolitem action='NodeBreakAction' />"
261 " <separator />"
262 " <toolitem action='NodeJoinSegmentAction' />"
263 " <toolitem action='NodeDeleteSegmentAction' />"
264 " <separator />"
265 " <toolitem action='NodeCuspAction' />"
266 " <toolitem action='NodeSmoothAction' />"
267 " <toolitem action='NodeSymmetricAction' />"
268 " <separator />"
269 " <toolitem action='NodeLineAction' />"
270 " <toolitem action='NodeCurveAction' />"
271 " <separator />"
272 " <toolitem action='ObjectToPath' />"
273 " <toolitem action='StrokeToPath' />"
274 " <separator />"
275 " <toolitem action='NodeXAction' />"
276 " <toolitem action='NodeYAction' />"
277 " <toolitem action='NodeUnitsAction' />"
278 " <separator />"
279 " <toolitem action='ObjectEditClipPathAction' />"
280 " <toolitem action='ObjectEditMaskPathAction' />"
281 " <toolitem action='EditNextLPEParameterAction' />"
282 " <separator />"
283 " <toolitem action='NodesShowHandlesAction' />"
284 " <toolitem action='NodesShowHelperpath' />"
285 " </toolbar>"
287 " <toolbar name='TweakToolbar'>"
288 " <toolitem action='TweakWidthAction' />"
289 " <separator />"
290 " <toolitem action='TweakForceAction' />"
291 " <toolitem action='TweakPressureAction' />"
292 " <separator />"
293 " <toolitem action='TweakModeAction' />"
294 " <separator />"
295 " <toolitem action='TweakFidelityAction' />"
296 " <separator />"
297 " <toolitem action='TweakChannelsLabel' />"
298 " <toolitem action='TweakDoH' />"
299 " <toolitem action='TweakDoS' />"
300 " <toolitem action='TweakDoL' />"
301 " <toolitem action='TweakDoO' />"
302 " </toolbar>"
304 " <toolbar name='ZoomToolbar'>"
305 " <toolitem action='ZoomIn' />"
306 " <toolitem action='ZoomOut' />"
307 " <separator />"
308 " <toolitem action='Zoom1:0' />"
309 " <toolitem action='Zoom1:2' />"
310 " <toolitem action='Zoom2:1' />"
311 " <separator />"
312 " <toolitem action='ZoomSelection' />"
313 " <toolitem action='ZoomDrawing' />"
314 " <toolitem action='ZoomPage' />"
315 " <toolitem action='ZoomPageWidth' />"
316 " <separator />"
317 " <toolitem action='ZoomPrev' />"
318 " <toolitem action='ZoomNext' />"
319 " </toolbar>"
321 " <toolbar name='StarToolbar'>"
322 " <separator />"
323 " <toolitem action='StarStateAction' />"
324 " <separator />"
325 " <toolitem action='FlatAction' />"
326 " <separator />"
327 " <toolitem action='MagnitudeAction' />"
328 " <toolitem action='SpokeAction' />"
329 " <toolitem action='RoundednessAction' />"
330 " <toolitem action='RandomizationAction' />"
331 " <separator />"
332 " <toolitem action='StarResetAction' />"
333 " </toolbar>"
335 " <toolbar name='RectToolbar'>"
336 " <toolitem action='RectStateAction' />"
337 " <toolitem action='RectWidthAction' />"
338 " <toolitem action='RectHeightAction' />"
339 " <toolitem action='RadiusXAction' />"
340 " <toolitem action='RadiusYAction' />"
341 " <toolitem action='RectUnitsAction' />"
342 " <separator />"
343 " <toolitem action='RectResetAction' />"
344 " </toolbar>"
346 " <toolbar name='3DBoxToolbar'>"
347 " <toolitem action='3DBoxAngleXAction' />"
348 " <toolitem action='3DBoxVPXStateAction' />"
349 " <separator />"
350 " <toolitem action='3DBoxAngleYAction' />"
351 " <toolitem action='3DBoxVPYStateAction' />"
352 " <separator />"
353 " <toolitem action='3DBoxAngleZAction' />"
354 " <toolitem action='3DBoxVPZStateAction' />"
355 " </toolbar>"
357 " <toolbar name='SpiralToolbar'>"
358 " <toolitem action='SpiralStateAction' />"
359 " <toolitem action='SpiralRevolutionAction' />"
360 " <toolitem action='SpiralExpansionAction' />"
361 " <toolitem action='SpiralT0Action' />"
362 " <separator />"
363 " <toolitem action='SpiralResetAction' />"
364 " </toolbar>"
366 " <toolbar name='PenToolbar'>"
367 " <toolitem action='FreehandModeActionPenTemp' />"
368 " <toolitem action='FreehandModeActionPen' />"
369 " </toolbar>"
371 " <toolbar name='PencilToolbar'>"
372 " <toolitem action='FreehandModeActionPencilTemp' />"
373 " <toolitem action='FreehandModeActionPencil' />"
374 " <separator />"
375 " <toolitem action='PencilToleranceAction' />"
376 " <separator />"
377 " <toolitem action='PencilResetAction' />"
378 " </toolbar>"
380 " <toolbar name='CalligraphyToolbar'>"
381 " <separator />"
382 " <toolitem action='SetProfileAction'/>"
383 " <toolitem action='SaveDeleteProfileAction'/>"
384 " <separator />"
385 " <toolitem action='CalligraphyWidthAction' />"
386 " <toolitem action='PressureAction' />"
387 " <toolitem action='TraceAction' />"
388 " <toolitem action='ThinningAction' />"
389 " <separator />"
390 " <toolitem action='AngleAction' />"
391 " <toolitem action='TiltAction' />"
392 " <toolitem action='FixationAction' />"
393 " <separator />"
394 " <toolitem action='CapRoundingAction' />"
395 " <separator />"
396 " <toolitem action='TremorAction' />"
397 " <toolitem action='WiggleAction' />"
398 " <toolitem action='MassAction' />"
399 " <separator />"
400 " </toolbar>"
402 " <toolbar name='ArcToolbar'>"
403 " <toolitem action='ArcStateAction' />"
404 " <separator />"
405 " <toolitem action='ArcStartAction' />"
406 " <toolitem action='ArcEndAction' />"
407 " <separator />"
408 " <toolitem action='ArcOpenAction' />"
409 " <separator />"
410 " <toolitem action='ArcResetAction' />"
411 " <separator />"
412 " </toolbar>"
414 " <toolbar name='PaintbucketToolbar'>"
415 " <toolitem action='ChannelsAction' />"
416 " <separator />"
417 " <toolitem action='ThresholdAction' />"
418 " <separator />"
419 " <toolitem action='OffsetAction' />"
420 " <toolitem action='PaintbucketUnitsAction' />"
421 " <separator />"
422 " <toolitem action='AutoGapAction' />"
423 " <separator />"
424 " <toolitem action='PaintbucketResetAction' />"
425 " </toolbar>"
427 " <toolbar name='EraserToolbar'>"
428 " <toolitem action='EraserWidthAction' />"
429 " <separator />"
430 " <toolitem action='EraserModeAction' />"
431 " </toolbar>"
433 " <toolbar name='DropperToolbar'>"
434 " <toolitem action='DropperOpacityAction' />"
435 " <toolitem action='DropperPickAlphaAction' />"
436 " <toolitem action='DropperSetAlphaAction' />"
437 " </toolbar>"
439 " <toolbar name='ConnectorToolbar'>"
440 " <toolitem action='ConnectorAvoidAction' />"
441 " <toolitem action='ConnectorIgnoreAction' />"
442 " <toolitem action='ConnectorSpacingAction' />"
443 " <toolitem action='ConnectorGraphAction' />"
444 " <toolitem action='ConnectorLengthAction' />"
445 " <toolitem action='ConnectorDirectedAction' />"
446 " <toolitem action='ConnectorOverlapAction' />"
447 " </toolbar>"
449 "</ui>"
450 ;
452 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
454 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
456 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
457 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
459 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
460 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
462 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
463 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
465 /* Global text entry widgets necessary for update */
466 /* GtkWidget *dropper_rgb_entry,
467 *dropper_opacity_entry ; */
468 // should be made a private member once this is converted to class
470 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
471 connection->disconnect();
472 delete connection;
473 }
475 static void purge_repr_listener( GObject* obj, GObject* tbl )
476 {
477 (void)obj;
478 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
479 if (oldrepr) { // remove old listener
480 sp_repr_remove_listener_by_data(oldrepr, tbl);
481 Inkscape::GC::release(oldrepr);
482 oldrepr = 0;
483 g_object_set_data( tbl, "repr", NULL );
484 }
485 }
487 GtkWidget *
488 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
489 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
490 Inkscape::UI::View::View *view, GtkTooltips *tt)
491 {
492 SPAction *action = verb->get_action(view);
493 if (!action) return NULL;
495 SPAction *doubleclick_action;
496 if (doubleclick_verb)
497 doubleclick_action = doubleclick_verb->get_action(view);
498 else
499 doubleclick_action = NULL;
501 /* fixme: Handle sensitive/unsensitive */
502 /* fixme: Implement sp_button_new_from_action */
503 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
504 gtk_widget_show(b);
507 unsigned int shortcut = sp_shortcut_get_primary(verb);
508 if (shortcut) {
509 gchar key[256];
510 sp_ui_shortcut_string(shortcut, key);
511 gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
512 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
513 g_free(tip);
514 } else {
515 gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
516 }
518 return b;
519 }
522 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
523 {
524 SPAction* targetAction = SP_ACTION(user_data);
525 if ( targetAction ) {
526 sp_action_perform( targetAction, NULL );
527 }
528 }
530 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
531 {
532 if ( data ) {
533 GtkAction* act = GTK_ACTION(data);
534 gtk_action_set_sensitive( act, sensitive );
535 }
536 }
538 static SPActionEventVector action_event_vector = {
539 {NULL},
540 NULL,
541 NULL,
542 sp_action_action_set_sensitive,
543 NULL,
544 NULL
545 };
547 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
548 {
549 GtkAction* act = 0;
551 SPAction* targetAction = verb->get_action(view);
552 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
553 act = GTK_ACTION(inky);
554 gtk_action_set_sensitive( act, targetAction->sensitive );
556 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
558 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
559 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
561 return act;
562 }
564 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
565 {
566 Inkscape::UI::View::View *view = desktop;
567 gint verbsToUse[] = {
568 // disabled until we have icons for them:
569 //find
570 //SP_VERB_EDIT_TILE,
571 //SP_VERB_EDIT_UNTILE,
572 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
573 SP_VERB_DIALOG_DISPLAY,
574 SP_VERB_DIALOG_FILL_STROKE,
575 SP_VERB_DIALOG_NAMEDVIEW,
576 SP_VERB_DIALOG_TEXT,
577 SP_VERB_DIALOG_XML_EDITOR,
578 SP_VERB_EDIT_CLONE,
579 SP_VERB_EDIT_COPY,
580 SP_VERB_EDIT_CUT,
581 SP_VERB_EDIT_DUPLICATE,
582 SP_VERB_EDIT_PASTE,
583 SP_VERB_EDIT_REDO,
584 SP_VERB_EDIT_UNDO,
585 SP_VERB_EDIT_UNLINK_CLONE,
586 SP_VERB_FILE_EXPORT,
587 SP_VERB_FILE_IMPORT,
588 SP_VERB_FILE_NEW,
589 SP_VERB_FILE_OPEN,
590 SP_VERB_FILE_PRINT,
591 SP_VERB_FILE_SAVE,
592 SP_VERB_OBJECT_TO_CURVE,
593 SP_VERB_SELECTION_GROUP,
594 SP_VERB_SELECTION_OUTLINE,
595 SP_VERB_SELECTION_UNGROUP,
596 SP_VERB_ZOOM_1_1,
597 SP_VERB_ZOOM_1_2,
598 SP_VERB_ZOOM_2_1,
599 SP_VERB_ZOOM_DRAWING,
600 SP_VERB_ZOOM_IN,
601 SP_VERB_ZOOM_NEXT,
602 SP_VERB_ZOOM_OUT,
603 SP_VERB_ZOOM_PAGE,
604 SP_VERB_ZOOM_PAGE_WIDTH,
605 SP_VERB_ZOOM_PREV,
606 SP_VERB_ZOOM_SELECTION,
607 };
609 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
611 static std::map<SPDesktop*, GtkActionGroup*> groups;
612 GtkActionGroup* mainActions = 0;
613 if ( groups.find(desktop) != groups.end() ) {
614 mainActions = groups[desktop];
615 }
617 if ( !mainActions ) {
618 mainActions = gtk_action_group_new("main");
619 groups[desktop] = mainActions;
620 }
622 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
623 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
624 if ( verb ) {
625 if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
626 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
627 gtk_action_group_add_action( mainActions, act );
628 }
629 }
630 }
632 return mainActions;
633 }
636 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
637 {
638 gtk_widget_set_size_request( widget,
639 widget->allocation.width,
640 widget->allocation.height );
641 }
643 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
644 {
645 gtk_widget_set_size_request( widget, -1, -1 );
646 }
650 GtkWidget *
651 sp_tool_toolbox_new()
652 {
653 GtkTooltips *tt = gtk_tooltips_new();
654 GtkWidget* tb = gtk_toolbar_new();
655 gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
656 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
658 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
659 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
661 gtk_widget_set_sensitive(tb, FALSE);
663 GtkWidget *hb = gtk_handle_box_new();
664 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
665 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
666 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
668 gtk_container_add(GTK_CONTAINER(hb), tb);
669 gtk_widget_show(GTK_WIDGET(tb));
671 sigc::connection* conn = new sigc::connection;
672 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
674 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
675 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
677 return hb;
678 }
680 GtkWidget *
681 sp_aux_toolbox_new()
682 {
683 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
685 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
687 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
689 gtk_widget_set_sensitive(tb, FALSE);
691 GtkWidget *hb = gtk_handle_box_new();
692 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
693 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
694 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
696 gtk_container_add(GTK_CONTAINER(hb), tb);
697 gtk_widget_show(GTK_WIDGET(tb));
699 sigc::connection* conn = new sigc::connection;
700 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
702 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
703 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
705 return hb;
706 }
708 //####################################
709 //# Commands Bar
710 //####################################
712 GtkWidget *
713 sp_commands_toolbox_new()
714 {
715 GtkWidget *tb = gtk_toolbar_new();
717 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
718 gtk_widget_set_sensitive(tb, FALSE);
720 GtkWidget *hb = gtk_handle_box_new();
721 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
722 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
723 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
725 gtk_container_add(GTK_CONTAINER(hb), tb);
726 gtk_widget_show(GTK_WIDGET(tb));
728 sigc::connection* conn = new sigc::connection;
729 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
731 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
732 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
734 return hb;
735 }
738 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
739 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
740 gchar const *path, gchar const *data, gdouble def,
741 GtkWidget *focusTarget,
742 GtkWidget *us,
743 GObject *dataKludge,
744 gboolean altx, gchar const *altx_mark,
745 gdouble lower, gdouble upper, gdouble step, gdouble page,
746 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
747 void (*callback)(GtkAdjustment *, GObject *),
748 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
749 {
750 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
751 lower, upper, step, page, page ) );
752 if (us) {
753 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
754 }
756 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
758 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
759 if ( shortLabel ) {
760 g_object_set( act, "short_label", shortLabel, NULL );
761 }
763 if ( (descrCount > 0) && descrLabels && descrValues ) {
764 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
765 }
767 if ( focusTarget ) {
768 ege_adjustment_action_set_focuswidget( act, focusTarget );
769 }
771 if ( altx && altx_mark ) {
772 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
773 }
775 if ( dataKludge ) {
776 g_object_set_data( dataKludge, data, adj );
777 }
779 // Using a cast just to make sure we pass in the right kind of function pointer
780 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
782 return act;
783 }
786 //####################################
787 //# node editing callbacks
788 //####################################
790 /**
791 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
792 */
793 static ShapeEditor *get_current_shape_editor()
794 {
795 if (!SP_ACTIVE_DESKTOP) {
796 return NULL;
797 }
799 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
801 if (!SP_IS_NODE_CONTEXT(event_context)) {
802 return NULL;
803 }
805 return SP_NODE_CONTEXT(event_context)->shape_editor;
806 }
809 void
810 sp_node_path_edit_add(void)
811 {
812 ShapeEditor *shape_editor = get_current_shape_editor();
813 if (shape_editor) shape_editor->add_node();
814 }
816 void
817 sp_node_path_edit_delete(void)
818 {
819 ShapeEditor *shape_editor = get_current_shape_editor();
820 if (shape_editor) shape_editor->delete_nodes_preserving_shape();
821 }
823 void
824 sp_node_path_edit_delete_segment(void)
825 {
826 ShapeEditor *shape_editor = get_current_shape_editor();
827 if (shape_editor) shape_editor->delete_segment();
828 }
830 void
831 sp_node_path_edit_break(void)
832 {
833 ShapeEditor *shape_editor = get_current_shape_editor();
834 if (shape_editor) shape_editor->break_at_nodes();
835 }
837 void
838 sp_node_path_edit_join(void)
839 {
840 ShapeEditor *shape_editor = get_current_shape_editor();
841 if (shape_editor) shape_editor->join_nodes();
842 }
844 void
845 sp_node_path_edit_join_segment(void)
846 {
847 ShapeEditor *shape_editor = get_current_shape_editor();
848 if (shape_editor) shape_editor->join_segments();
849 }
851 void
852 sp_node_path_edit_toline(void)
853 {
854 ShapeEditor *shape_editor = get_current_shape_editor();
855 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
856 }
858 void
859 sp_node_path_edit_tocurve(void)
860 {
861 ShapeEditor *shape_editor = get_current_shape_editor();
862 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
863 }
865 void
866 sp_node_path_edit_cusp(void)
867 {
868 ShapeEditor *shape_editor = get_current_shape_editor();
869 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
870 }
872 void
873 sp_node_path_edit_smooth(void)
874 {
875 ShapeEditor *shape_editor = get_current_shape_editor();
876 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
877 }
879 void
880 sp_node_path_edit_symmetrical(void)
881 {
882 ShapeEditor *shape_editor = get_current_shape_editor();
883 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
884 }
886 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
887 bool show = gtk_toggle_action_get_active( act );
888 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
889 ShapeEditor *shape_editor = get_current_shape_editor();
890 if (shape_editor) shape_editor->show_handles(show);
891 }
893 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
894 bool show = gtk_toggle_action_get_active( act );
895 prefs_set_int_attribute ("tools.nodes", "show_helperpath", show ? 1 : 0);
896 ShapeEditor *shape_editor = get_current_shape_editor();
897 if (shape_editor) shape_editor->show_helperpath(show);
898 }
900 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
901 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
902 }
904 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
905 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
906 }
908 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
909 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
910 }
912 /* is called when the node selection is modified */
913 static void
914 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
915 {
916 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
917 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
918 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
919 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
921 // quit if run by the attr_changed listener
922 if (g_object_get_data( tbl, "freeze" )) {
923 return;
924 }
926 // in turn, prevent listener from responding
927 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
929 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
930 SPUnit const *unit = tracker->getActiveUnit();
932 ShapeEditor *shape_editor = get_current_shape_editor();
933 if (shape_editor && shape_editor->has_nodepath()) {
934 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
935 int n_selected = 0;
936 if (nodepath) {
937 n_selected = nodepath->numSelected();
938 }
940 if (n_selected == 0) {
941 gtk_action_set_sensitive(xact, FALSE);
942 gtk_action_set_sensitive(yact, FALSE);
943 } else {
944 gtk_action_set_sensitive(xact, TRUE);
945 gtk_action_set_sensitive(yact, TRUE);
946 NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
947 NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
949 if (n_selected == 1) {
950 NR::Point sel_node = nodepath->singleSelectedCoords();
951 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
952 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
953 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
954 }
955 } else {
956 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
957 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
958 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
959 /* Note: Currently x and y will always have a value, even if the coordinates of the
960 selected nodes don't coincide (in this case we use the coordinates of the center
961 of the bounding box). So the entries are never set to zero. */
962 // FIXME: Maybe we should clear the entry if several nodes are selected
963 // instead of providing a kind of average value
964 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
965 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
966 }
967 }
968 }
969 } else {
970 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
971 gtk_action_set_sensitive(xact, FALSE);
972 gtk_action_set_sensitive(yact, FALSE);
973 }
975 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
976 }
978 static void
979 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
980 {
981 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
983 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
984 SPUnit const *unit = tracker->getActiveUnit();
986 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
987 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
988 }
990 // quit if run by the attr_changed listener
991 if (g_object_get_data( tbl, "freeze" )) {
992 return;
993 }
995 // in turn, prevent listener from responding
996 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
998 ShapeEditor *shape_editor = get_current_shape_editor();
999 if (shape_editor && shape_editor->has_nodepath()) {
1000 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1001 if (!strcmp(value_name, "x")) {
1002 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
1003 }
1004 if (!strcmp(value_name, "y")) {
1005 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
1006 }
1007 }
1009 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1010 }
1012 static void
1013 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1014 {
1015 sp_node_path_value_changed(adj, tbl, "x");
1016 }
1018 static void
1019 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1020 {
1021 sp_node_path_value_changed(adj, tbl, "y");
1022 }
1024 void
1025 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1026 {
1027 {
1028 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1029 SPItem *item = selection->singleItem();
1030 if (item && SP_IS_LPE_ITEM(item)) {
1031 if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1032 gtk_action_set_sensitive(w, TRUE);
1033 } else {
1034 gtk_action_set_sensitive(w, FALSE);
1035 }
1036 } else {
1037 gtk_action_set_sensitive(w, FALSE);
1038 }
1039 }
1041 {
1042 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1043 SPItem *item = selection->singleItem();
1044 if (item && item->clip_ref && item->clip_ref->getObject()) {
1045 gtk_action_set_sensitive(w, TRUE);
1046 } else {
1047 gtk_action_set_sensitive(w, FALSE);
1048 }
1049 }
1051 {
1052 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1053 SPItem *item = selection->singleItem();
1054 if (item && item->mask_ref && item->mask_ref->getObject()) {
1055 gtk_action_set_sensitive(w, TRUE);
1056 } else {
1057 gtk_action_set_sensitive(w, FALSE);
1058 }
1059 }
1060 }
1062 void
1063 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1064 {
1065 sp_node_toolbox_sel_changed (selection, tbl);
1066 }
1070 //################################
1071 //## Node Editing Toolbox ##
1072 //################################
1074 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1075 {
1076 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1077 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1078 g_object_set_data( holder, "tracker", tracker );
1080 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1082 {
1083 InkAction* inky = ink_action_new( "NodeInsertAction",
1084 _("Insert node"),
1085 _("Insert new nodes into selected segments"),
1086 "node_insert",
1087 secondarySize );
1088 g_object_set( inky, "short_label", _("Insert"), NULL );
1089 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1090 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1091 }
1093 {
1094 InkAction* inky = ink_action_new( "NodeDeleteAction",
1095 _("Delete node"),
1096 _("Delete selected nodes"),
1097 "node_delete",
1098 secondarySize );
1099 g_object_set( inky, "short_label", _("Delete"), NULL );
1100 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1101 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1102 }
1104 {
1105 InkAction* inky = ink_action_new( "NodeJoinAction",
1106 _("Join endnodes"),
1107 _("Join selected endnodes"),
1108 "node_join",
1109 secondarySize );
1110 g_object_set( inky, "short_label", _("Join"), NULL );
1111 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1112 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1113 }
1115 {
1116 InkAction* inky = ink_action_new( "NodeBreakAction",
1117 _("Break nodes"),
1118 _("Break path at selected nodes"),
1119 "node_break",
1120 secondarySize );
1121 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1122 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1123 }
1126 {
1127 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1128 _("Join with segment"),
1129 _("Join selected endnodes with a new segment"),
1130 "node_join_segment",
1131 secondarySize );
1132 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1133 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1134 }
1136 {
1137 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1138 _("Delete segment"),
1139 _("Delete segment between two non-endpoint nodes"),
1140 "node_delete_segment",
1141 secondarySize );
1142 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1143 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1144 }
1146 {
1147 InkAction* inky = ink_action_new( "NodeCuspAction",
1148 _("Node Cusp"),
1149 _("Make selected nodes corner"),
1150 "node_cusp",
1151 secondarySize );
1152 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1153 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1154 }
1156 {
1157 InkAction* inky = ink_action_new( "NodeSmoothAction",
1158 _("Node Smooth"),
1159 _("Make selected nodes smooth"),
1160 "node_smooth",
1161 secondarySize );
1162 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1163 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1164 }
1166 {
1167 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1168 _("Node Symmetric"),
1169 _("Make selected nodes symmetric"),
1170 "node_symmetric",
1171 secondarySize );
1172 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1173 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1174 }
1176 {
1177 InkAction* inky = ink_action_new( "NodeLineAction",
1178 _("Node Line"),
1179 _("Make selected segments lines"),
1180 "node_line",
1181 secondarySize );
1182 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1183 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1184 }
1186 {
1187 InkAction* inky = ink_action_new( "NodeCurveAction",
1188 _("Node Curve"),
1189 _("Make selected segments curves"),
1190 "node_curve",
1191 secondarySize );
1192 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1193 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1194 }
1196 {
1197 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1198 _("Show Handles"),
1199 _("Show the Bezier handles of selected nodes"),
1200 "nodes_show_handles",
1201 Inkscape::ICON_SIZE_DECORATION );
1202 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1203 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1204 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1205 }
1207 {
1208 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1209 _("Show Outline"),
1210 _("Show the outline of the path"),
1211 "nodes_show_helperpath",
1212 Inkscape::ICON_SIZE_DECORATION );
1213 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1214 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1215 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1216 }
1218 {
1219 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1220 _("Next path effect parameter"),
1221 _("Show next path effect parameter for editing"),
1222 "edit_next_parameter",
1223 Inkscape::ICON_SIZE_DECORATION );
1224 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1225 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1226 g_object_set_data( holder, "nodes_lpeedit", inky);
1227 }
1229 {
1230 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1231 _("Edit clipping path"),
1232 _("Edit the clipping path of the object"),
1233 "nodeedit-clippath",
1234 Inkscape::ICON_SIZE_DECORATION );
1235 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1236 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1237 g_object_set_data( holder, "nodes_clippathedit", inky);
1238 }
1240 {
1241 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1242 _("Edit mask path"),
1243 _("Edit the mask of the object"),
1244 "nodeedit-mask",
1245 Inkscape::ICON_SIZE_DECORATION );
1246 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1247 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1248 g_object_set_data( holder, "nodes_maskedit", inky);
1249 }
1251 /* X coord of selected node(s) */
1252 {
1253 EgeAdjustmentAction* eact = 0;
1254 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1255 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1256 eact = create_adjustment_action( "NodeXAction",
1257 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1258 "tools.nodes", "Xcoord", 0,
1259 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1260 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1261 labels, values, G_N_ELEMENTS(labels),
1262 sp_node_path_x_value_changed );
1263 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1264 g_object_set_data( holder, "nodes_x_action", eact );
1265 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1266 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1267 }
1269 /* Y coord of selected node(s) */
1270 {
1271 EgeAdjustmentAction* eact = 0;
1272 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1273 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1274 eact = create_adjustment_action( "NodeYAction",
1275 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1276 "tools.nodes", "Ycoord", 0,
1277 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1278 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1279 labels, values, G_N_ELEMENTS(labels),
1280 sp_node_path_y_value_changed );
1281 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1282 g_object_set_data( holder, "nodes_y_action", eact );
1283 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1284 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1285 }
1287 // add the units menu
1288 {
1289 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1290 gtk_action_group_add_action( mainActions, act );
1291 }
1294 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1296 //watch selection
1297 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1299 sigc::connection *c_selection_changed =
1300 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1301 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1302 pool->add_connection ("selection-changed", c_selection_changed);
1304 sigc::connection *c_selection_modified =
1305 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1306 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1307 pool->add_connection ("selection-modified", c_selection_modified);
1309 sigc::connection *c_subselection_changed =
1310 new sigc::connection (desktop->connectToolSubselectionChanged
1311 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1312 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1314 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1316 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1317 } // end of sp_node_toolbox_prep()
1320 //########################
1321 //## Zoom Toolbox ##
1322 //########################
1324 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1325 {
1326 // no custom GtkAction setup needed
1327 } // end of sp_zoom_toolbox_prep()
1329 void
1330 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1331 {
1332 if ( GTK_IS_BIN(toolbox) ) {
1333 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")));
1334 } else if (GTK_IS_TOOLBAR(toolbox) ) {
1335 toolbox_set_desktop(toolbox,
1336 desktop,
1337 setup_tool_toolbox,
1338 update_tool_toolbox,
1339 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1340 "event_context_connection")));
1341 } else {
1342 g_warning("Unexpected toolbox type");
1343 }
1344 }
1347 void
1348 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1349 {
1350 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1351 desktop,
1352 setup_aux_toolbox,
1353 update_aux_toolbox,
1354 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1355 "event_context_connection")));
1356 }
1358 void
1359 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1360 {
1361 toolbox_set_desktop(toolbox,
1362 desktop,
1363 setup_commands_toolbox,
1364 update_commands_toolbox,
1365 static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1366 "event_context_connection")));
1367 }
1369 static void
1370 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1371 {
1372 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1373 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1375 if (old_desktop) {
1376 GList *children, *iter;
1378 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1379 for ( iter = children ; iter ; iter = iter->next ) {
1380 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1381 }
1382 g_list_free(children);
1383 }
1385 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1387 if (desktop) {
1388 gtk_widget_set_sensitive(toolbox, TRUE);
1389 setup_func(toolbox, desktop);
1390 update_func(desktop, desktop->event_context, toolbox);
1391 *conn = desktop->connectEventContextChanged
1392 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1393 } else {
1394 gtk_widget_set_sensitive(toolbox, FALSE);
1395 }
1397 } // end of toolbox_set_desktop()
1400 static void
1401 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1402 {
1403 GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1404 Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1406 for (int i = 0 ; tools[i].type_name ; i++ ) {
1407 GtkWidget *button =
1408 sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1409 SP_BUTTON_TYPE_TOGGLE,
1410 Inkscape::Verb::get(tools[i].verb),
1411 Inkscape::Verb::get(tools[i].doubleclick_verb),
1412 desktop,
1413 tooltips );
1415 g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1416 (gpointer)button );
1417 }
1418 }
1421 static void
1422 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1423 {
1424 gchar const *const tname = ( eventcontext
1425 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1426 : NULL );
1427 for (int i = 0 ; tools[i].type_name ; i++ ) {
1428 SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1429 sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1430 }
1431 }
1433 static void
1434 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1435 {
1436 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1437 GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1438 GtkUIManager* mgr = gtk_ui_manager_new();
1439 GError* errVal = 0;
1440 gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1441 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1443 std::map<std::string, GtkWidget*> dataHolders;
1445 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1446 if ( aux_toolboxes[i].prep_func ) {
1447 // converted to GtkActions and UIManager
1449 GtkWidget* kludge = gtk_toolbar_new();
1450 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1451 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1452 dataHolders[aux_toolboxes[i].type_name] = kludge;
1453 aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1454 } else {
1456 GtkWidget *sub_toolbox = 0;
1457 if (aux_toolboxes[i].create_func == NULL)
1458 sub_toolbox = sp_empty_toolbox_new(desktop);
1459 else {
1460 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1461 }
1463 gtk_size_group_add_widget( grouper, sub_toolbox );
1465 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1466 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1468 }
1469 }
1471 // Second pass to create toolbars *after* all GtkActions are created
1472 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1473 if ( aux_toolboxes[i].prep_func ) {
1474 // converted to GtkActions and UIManager
1476 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1478 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1479 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1481 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1482 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1483 g_free( tmp );
1484 tmp = 0;
1486 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1487 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1488 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1489 }
1490 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1493 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1495 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1496 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1497 swatch->setDesktop( desktop );
1498 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1499 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1500 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1501 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 );
1502 }
1504 gtk_widget_show_all( holder );
1505 sp_set_font_size_smaller( holder );
1507 gtk_size_group_add_widget( grouper, holder );
1509 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1510 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1511 }
1512 }
1514 g_object_unref( G_OBJECT(grouper) );
1515 }
1517 static void
1518 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1519 {
1520 gchar const *tname = ( eventcontext
1521 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1522 : NULL );
1523 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1524 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1525 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1526 gtk_widget_show_all(sub_toolbox);
1527 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1528 } else {
1529 gtk_widget_hide(sub_toolbox);
1530 }
1531 }
1532 }
1534 static void
1535 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1536 {
1537 gchar const * descr =
1538 "<ui>"
1539 " <toolbar name='CommandsToolbar'>"
1540 " <toolitem action='FileNew' />"
1541 " <toolitem action='FileOpen' />"
1542 " <toolitem action='FileSave' />"
1543 " <toolitem action='FilePrint' />"
1544 " <separator />"
1545 " <toolitem action='FileImport' />"
1546 " <toolitem action='FileExport' />"
1547 " <separator />"
1548 " <toolitem action='EditUndo' />"
1549 " <toolitem action='EditRedo' />"
1550 " <separator />"
1551 " <toolitem action='EditCopy' />"
1552 " <toolitem action='EditCut' />"
1553 " <toolitem action='EditPaste' />"
1554 " <separator />"
1555 " <toolitem action='ZoomSelection' />"
1556 " <toolitem action='ZoomDrawing' />"
1557 " <toolitem action='ZoomPage' />"
1558 " <separator />"
1559 " <toolitem action='EditDuplicate' />"
1560 " <toolitem action='EditClone' />"
1561 " <toolitem action='EditUnlinkClone' />"
1562 " <separator />"
1563 " <toolitem action='SelectionGroup' />"
1564 " <toolitem action='SelectionUnGroup' />"
1565 " <separator />"
1566 " <toolitem action='DialogFillStroke' />"
1567 " <toolitem action='DialogText' />"
1568 " <toolitem action='DialogXMLEditor' />"
1569 " <toolitem action='DialogAlignDistribute' />"
1570 " <separator />"
1571 " <toolitem action='DialogPreferences' />"
1572 " <toolitem action='DialogDocumentProperties' />"
1573 " </toolbar>"
1574 "</ui>";
1575 GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1578 GtkUIManager* mgr = gtk_ui_manager_new();
1579 GError* errVal = 0;
1581 gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1582 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1584 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1585 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1586 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1587 }
1589 Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1590 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1592 gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1593 gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1596 g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1598 GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1599 if ( child ) {
1600 gtk_container_remove( GTK_CONTAINER(toolbox), child );
1601 }
1603 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1604 }
1606 static void
1607 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1608 {
1609 }
1611 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1612 {
1613 gtk_widget_show(toolbox_toplevel);
1614 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1616 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1617 if (!shown_toolbox) {
1618 return;
1619 }
1620 gtk_widget_show(toolbox);
1622 gtk_widget_show_all(shown_toolbox);
1623 }
1625 static GtkWidget *
1626 sp_empty_toolbox_new(SPDesktop *desktop)
1627 {
1628 GtkWidget *tbl = gtk_toolbar_new();
1629 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1630 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1632 gtk_widget_show_all(tbl);
1633 sp_set_font_size_smaller (tbl);
1635 return tbl;
1636 }
1638 // helper UI functions
1640 GtkWidget *
1641 sp_tb_spinbutton(
1642 gchar *label, gchar const *tooltip,
1643 gchar const *path, gchar const *data, gdouble def,
1644 GtkWidget *us,
1645 GtkWidget *tbl,
1646 gboolean altx, gchar const *altx_mark,
1647 gdouble lower, gdouble upper, gdouble step, gdouble page,
1648 void (*callback)(GtkAdjustment *, GtkWidget *),
1649 gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1650 {
1651 GtkTooltips *tt = gtk_tooltips_new();
1653 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1655 GtkWidget *l = gtk_label_new(label);
1656 gtk_widget_show(l);
1657 gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1658 gtk_container_add(GTK_CONTAINER(hb), l);
1660 GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1661 lower, upper, step, page, page);
1662 gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1663 if (us)
1664 sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1666 GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1667 gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1668 if (altx)
1669 gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1670 gtk_widget_set_size_request(sb,
1671 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1672 AUX_SPINBUTTON_HEIGHT);
1673 gtk_widget_show(sb);
1674 gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1675 gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1676 gtk_container_add(GTK_CONTAINER(hb), sb);
1677 gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1679 return hb;
1680 }
1682 #define MODE_LABEL_WIDTH 70
1684 //########################
1685 //## Star ##
1686 //########################
1688 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1689 {
1690 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1692 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1693 // do not remember prefs if this call is initiated by an undo change, because undoing object
1694 // creation sets bogus values to its attributes before it is deleted
1695 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1696 }
1698 // quit if run by the attr_changed listener
1699 if (g_object_get_data( dataKludge, "freeze" )) {
1700 return;
1701 }
1703 // in turn, prevent listener from responding
1704 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1706 bool modmade = false;
1708 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1709 GSList const *items = selection->itemList();
1710 for (; items != NULL; items = items->next) {
1711 if (SP_IS_STAR((SPItem *) items->data)) {
1712 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1713 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1714 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1715 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1716 + M_PI / (gint)adj->value));
1717 SP_OBJECT((SPItem *) items->data)->updateRepr();
1718 modmade = true;
1719 }
1720 }
1721 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1722 _("Star: Change number of corners"));
1724 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1725 }
1727 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1728 {
1729 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1731 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1732 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1733 }
1735 // quit if run by the attr_changed listener
1736 if (g_object_get_data( dataKludge, "freeze" )) {
1737 return;
1738 }
1740 // in turn, prevent listener from responding
1741 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1743 bool modmade = false;
1744 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1745 GSList const *items = selection->itemList();
1746 for (; items != NULL; items = items->next) {
1747 if (SP_IS_STAR((SPItem *) items->data)) {
1748 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1750 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1751 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1752 if (r2 < r1) {
1753 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1754 } else {
1755 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1756 }
1758 SP_OBJECT((SPItem *) items->data)->updateRepr();
1759 modmade = true;
1760 }
1761 }
1763 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1764 _("Star: Change spoke ratio"));
1766 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1767 }
1769 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1770 {
1771 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1772 bool flat = ege_select_one_action_get_active( act ) == 0;
1774 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1775 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1776 flat ? "true" : "false" );
1777 }
1779 // quit if run by the attr_changed listener
1780 if (g_object_get_data( dataKludge, "freeze" )) {
1781 return;
1782 }
1784 // in turn, prevent listener from responding
1785 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1787 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1788 GSList const *items = selection->itemList();
1789 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1790 bool modmade = false;
1792 if ( prop_action ) {
1793 gtk_action_set_sensitive( prop_action, !flat );
1794 }
1796 for (; items != NULL; items = items->next) {
1797 if (SP_IS_STAR((SPItem *) items->data)) {
1798 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1799 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1800 SP_OBJECT((SPItem *) items->data)->updateRepr();
1801 modmade = true;
1802 }
1803 }
1805 if (modmade) {
1806 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1807 flat ? _("Make polygon") : _("Make star"));
1808 }
1810 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1811 }
1813 static void sp_stb_rounded_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", "rounded", (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:rounded", (gdouble) adj->value);
1837 SP_OBJECT(items->data)->updateRepr();
1838 modmade = true;
1839 }
1840 }
1841 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1842 _("Star: Change rounding"));
1844 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1845 }
1847 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1848 {
1849 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1851 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1852 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1853 }
1855 // quit if run by the attr_changed listener
1856 if (g_object_get_data( dataKludge, "freeze" )) {
1857 return;
1858 }
1860 // in turn, prevent listener from responding
1861 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1863 bool modmade = false;
1865 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1866 GSList const *items = selection->itemList();
1867 for (; items != NULL; items = items->next) {
1868 if (SP_IS_STAR((SPItem *) items->data)) {
1869 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1870 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1871 SP_OBJECT(items->data)->updateRepr();
1872 modmade = true;
1873 }
1874 }
1875 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1876 _("Star: Change randomization"));
1878 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1879 }
1882 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1883 gchar const */*old_value*/, gchar const */*new_value*/,
1884 bool /*is_interactive*/, gpointer data)
1885 {
1886 GtkWidget *tbl = GTK_WIDGET(data);
1888 // quit if run by the _changed callbacks
1889 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1890 return;
1891 }
1893 // in turn, prevent callbacks from responding
1894 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1896 GtkAdjustment *adj = 0;
1898 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1899 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1901 if (!strcmp(name, "inkscape:randomized")) {
1902 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1903 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1904 } else if (!strcmp(name, "inkscape:rounded")) {
1905 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1906 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1907 } else if (!strcmp(name, "inkscape:flatsided")) {
1908 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1909 char const *flatsides = repr->attribute("inkscape:flatsided");
1910 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1911 if ( flatsides && !strcmp(flatsides,"false") ) {
1912 ege_select_one_action_set_active( flat_action, 1 );
1913 gtk_action_set_sensitive( prop_action, TRUE );
1914 } else {
1915 ege_select_one_action_set_active( flat_action, 0 );
1916 gtk_action_set_sensitive( prop_action, FALSE );
1917 }
1918 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1919 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1920 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1921 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1922 if (r2 < r1) {
1923 gtk_adjustment_set_value(adj, r2/r1);
1924 } else {
1925 gtk_adjustment_set_value(adj, r1/r2);
1926 }
1927 } else if (!strcmp(name, "sodipodi:sides")) {
1928 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1929 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1930 }
1932 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1933 }
1936 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1937 {
1938 NULL, /* child_added */
1939 NULL, /* child_removed */
1940 star_tb_event_attr_changed,
1941 NULL, /* content_changed */
1942 NULL /* order_changed */
1943 };
1946 /**
1947 * \param selection Should not be NULL.
1948 */
1949 static void
1950 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1951 {
1952 int n_selected = 0;
1953 Inkscape::XML::Node *repr = NULL;
1955 purge_repr_listener( tbl, tbl );
1957 for (GSList const *items = selection->itemList();
1958 items != NULL;
1959 items = items->next)
1960 {
1961 if (SP_IS_STAR((SPItem *) items->data)) {
1962 n_selected++;
1963 repr = SP_OBJECT_REPR((SPItem *) items->data);
1964 }
1965 }
1967 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1969 if (n_selected == 0) {
1970 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1971 } else if (n_selected == 1) {
1972 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1974 if (repr) {
1975 g_object_set_data( tbl, "repr", repr );
1976 Inkscape::GC::anchor(repr);
1977 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1978 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1979 }
1980 } else {
1981 // FIXME: implement averaging of all parameters for multiple selected stars
1982 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1983 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1984 }
1985 }
1988 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1989 {
1990 // FIXME: in this and all other _default functions, set some flag telling the value_changed
1991 // callbacks to lump all the changes for all selected objects in one undo step
1993 GtkAdjustment *adj = 0;
1995 // fixme: make settable in prefs!
1996 gint mag = 5;
1997 gdouble prop = 0.5;
1998 gboolean flat = FALSE;
1999 gdouble randomized = 0;
2000 gdouble rounded = 0;
2002 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2003 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2005 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2006 gtk_action_set_sensitive( sb2, !flat );
2008 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2009 gtk_adjustment_set_value(adj, mag);
2010 gtk_adjustment_value_changed(adj);
2012 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2013 gtk_adjustment_set_value(adj, prop);
2014 gtk_adjustment_value_changed(adj);
2016 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2017 gtk_adjustment_set_value(adj, rounded);
2018 gtk_adjustment_value_changed(adj);
2020 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2021 gtk_adjustment_set_value(adj, randomized);
2022 gtk_adjustment_value_changed(adj);
2023 }
2026 void
2027 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2028 {
2029 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2030 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2031 GtkWidget *l = gtk_label_new(NULL);
2032 gtk_label_set_markup(GTK_LABEL(l), title);
2033 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2034 if ( GTK_IS_TOOLBAR(tbl) ) {
2035 gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2036 } else {
2037 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2038 }
2039 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2040 }
2043 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2044 {
2045 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2047 {
2048 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2049 ege_output_action_set_use_markup( act, TRUE );
2050 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2051 g_object_set_data( holder, "mode_action", act );
2052 }
2054 {
2055 EgeAdjustmentAction* eact = 0;
2056 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2057 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2059 /* Flatsided checkbox */
2060 {
2061 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2063 GtkTreeIter iter;
2064 gtk_list_store_append( model, &iter );
2065 gtk_list_store_set( model, &iter,
2066 0, _("Polygon"),
2067 1, _("Regular polygon (with one handle) instead of a star"),
2068 2, "star_flat",
2069 -1 );
2071 gtk_list_store_append( model, &iter );
2072 gtk_list_store_set( model, &iter,
2073 0, _("Star"),
2074 1, _("Star instead of a regular polygon (with one handle)"),
2075 2, "star_angled",
2076 -1 );
2078 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2079 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2080 g_object_set_data( holder, "flat_action", act );
2082 ege_select_one_action_set_appearance( act, "full" );
2083 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2084 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2085 ege_select_one_action_set_icon_column( act, 2 );
2086 ege_select_one_action_set_icon_size( act, secondarySize );
2087 ege_select_one_action_set_tooltip_column( act, 1 );
2089 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2090 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2091 }
2093 /* Magnitude */
2094 {
2095 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2096 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2097 eact = create_adjustment_action( "MagnitudeAction",
2098 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2099 "tools.shapes.star", "magnitude", 3,
2100 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2101 3, 1024, 1, 5,
2102 labels, values, G_N_ELEMENTS(labels),
2103 sp_stb_magnitude_value_changed,
2104 1.0, 0 );
2105 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2106 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2107 }
2109 /* Spoke ratio */
2110 {
2111 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2112 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2113 eact = create_adjustment_action( "SpokeAction",
2114 _("Spoke ratio"), _("Spoke ratio:"),
2115 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2116 // Base radius is the same for the closest handle.
2117 _("Base radius to tip radius ratio"),
2118 "tools.shapes.star", "proportion", 0.5,
2119 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2120 0.01, 1.0, 0.01, 0.1,
2121 labels, values, G_N_ELEMENTS(labels),
2122 sp_stb_proportion_value_changed );
2123 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2124 g_object_set_data( holder, "prop_action", eact );
2125 }
2127 if ( !isFlatSided ) {
2128 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2129 } else {
2130 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2131 }
2133 /* Roundedness */
2134 {
2135 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2136 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2137 eact = create_adjustment_action( "RoundednessAction",
2138 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2139 "tools.shapes.star", "rounded", 0.0,
2140 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2141 -10.0, 10.0, 0.01, 0.1,
2142 labels, values, G_N_ELEMENTS(labels),
2143 sp_stb_rounded_value_changed );
2144 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2145 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2146 }
2148 /* Randomization */
2149 {
2150 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2151 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2152 eact = create_adjustment_action( "RandomizationAction",
2153 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2154 "tools.shapes.star", "randomized", 0.0,
2155 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2156 -10.0, 10.0, 0.001, 0.01,
2157 labels, values, G_N_ELEMENTS(labels),
2158 sp_stb_randomized_value_changed, 0.1, 3 );
2159 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2160 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2161 }
2162 }
2164 {
2165 /* Reset */
2166 {
2167 GtkAction* act = gtk_action_new( "StarResetAction",
2168 _("Defaults"),
2169 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2170 GTK_STOCK_CLEAR );
2171 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2172 gtk_action_group_add_action( mainActions, act );
2173 gtk_action_set_sensitive( act, TRUE );
2174 }
2175 }
2177 sigc::connection *connection = new sigc::connection(
2178 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2179 );
2180 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2181 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2182 }
2185 //########################
2186 //## Rect ##
2187 //########################
2189 static void sp_rtb_sensitivize( GObject *tbl )
2190 {
2191 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2192 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2193 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2195 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2196 gtk_action_set_sensitive( not_rounded, FALSE );
2197 } else {
2198 gtk_action_set_sensitive( not_rounded, TRUE );
2199 }
2200 }
2203 static void
2204 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2205 void (*setter)(SPRect *, gdouble))
2206 {
2207 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2209 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2210 SPUnit const *unit = tracker->getActiveUnit();
2212 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2213 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2214 }
2216 // quit if run by the attr_changed listener
2217 if (g_object_get_data( tbl, "freeze" )) {
2218 return;
2219 }
2221 // in turn, prevent listener from responding
2222 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2224 bool modmade = false;
2225 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2226 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2227 if (SP_IS_RECT(items->data)) {
2228 if (adj->value != 0) {
2229 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2230 } else {
2231 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2232 }
2233 modmade = true;
2234 }
2235 }
2237 sp_rtb_sensitivize( tbl );
2239 if (modmade) {
2240 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2241 _("Change rectangle"));
2242 }
2244 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2245 }
2247 static void
2248 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2249 {
2250 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2251 }
2253 static void
2254 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2255 {
2256 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2257 }
2259 static void
2260 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2261 {
2262 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2263 }
2265 static void
2266 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2267 {
2268 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2269 }
2273 static void
2274 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2275 {
2276 GtkAdjustment *adj = 0;
2278 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2279 gtk_adjustment_set_value(adj, 0.0);
2280 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2281 gtk_adjustment_value_changed(adj);
2283 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2284 gtk_adjustment_set_value(adj, 0.0);
2285 gtk_adjustment_value_changed(adj);
2287 sp_rtb_sensitivize( obj );
2288 }
2290 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2291 gchar const */*old_value*/, gchar const */*new_value*/,
2292 bool /*is_interactive*/, gpointer data)
2293 {
2294 GObject *tbl = G_OBJECT(data);
2296 // quit if run by the _changed callbacks
2297 if (g_object_get_data( tbl, "freeze" )) {
2298 return;
2299 }
2301 // in turn, prevent callbacks from responding
2302 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2304 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2305 SPUnit const *unit = tracker->getActiveUnit();
2307 gpointer item = g_object_get_data( tbl, "item" );
2308 if (item && SP_IS_RECT(item)) {
2309 {
2310 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2311 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2312 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2313 }
2315 {
2316 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2317 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2318 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2319 }
2321 {
2322 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2323 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2324 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2325 }
2327 {
2328 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2329 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2330 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2331 }
2332 }
2334 sp_rtb_sensitivize( tbl );
2336 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2337 }
2340 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2341 NULL, /* child_added */
2342 NULL, /* child_removed */
2343 rect_tb_event_attr_changed,
2344 NULL, /* content_changed */
2345 NULL /* order_changed */
2346 };
2348 /**
2349 * \param selection should not be NULL.
2350 */
2351 static void
2352 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2353 {
2354 int n_selected = 0;
2355 Inkscape::XML::Node *repr = NULL;
2356 SPItem *item = NULL;
2358 if ( g_object_get_data( tbl, "repr" ) ) {
2359 g_object_set_data( tbl, "item", NULL );
2360 }
2361 purge_repr_listener( tbl, tbl );
2363 for (GSList const *items = selection->itemList();
2364 items != NULL;
2365 items = items->next) {
2366 if (SP_IS_RECT((SPItem *) items->data)) {
2367 n_selected++;
2368 item = (SPItem *) items->data;
2369 repr = SP_OBJECT_REPR(item);
2370 }
2371 }
2373 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2375 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2377 if (n_selected == 0) {
2378 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2380 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2381 gtk_action_set_sensitive(w, FALSE);
2382 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2383 gtk_action_set_sensitive(h, FALSE);
2385 } else if (n_selected == 1) {
2386 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2387 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2389 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2390 gtk_action_set_sensitive(w, TRUE);
2391 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2392 gtk_action_set_sensitive(h, TRUE);
2394 if (repr) {
2395 g_object_set_data( tbl, "repr", repr );
2396 g_object_set_data( tbl, "item", item );
2397 Inkscape::GC::anchor(repr);
2398 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2399 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2400 }
2401 } else {
2402 // FIXME: implement averaging of all parameters for multiple selected
2403 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2404 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2405 sp_rtb_sensitivize( tbl );
2406 }
2407 }
2410 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2411 {
2412 EgeAdjustmentAction* eact = 0;
2413 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2415 {
2416 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2417 ege_output_action_set_use_markup( act, TRUE );
2418 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2419 g_object_set_data( holder, "mode_action", act );
2420 }
2422 // rx/ry units menu: create
2423 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2424 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2425 // fixme: add % meaning per cent of the width/height
2426 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2427 g_object_set_data( holder, "tracker", tracker );
2429 /* W */
2430 {
2431 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2432 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2433 eact = create_adjustment_action( "RectWidthAction",
2434 _("Width"), _("W:"), _("Width of rectangle"),
2435 "tools.shapes.rect", "width", 0,
2436 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2437 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2438 labels, values, G_N_ELEMENTS(labels),
2439 sp_rtb_width_value_changed );
2440 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2441 g_object_set_data( holder, "width_action", eact );
2442 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2443 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2444 }
2446 /* H */
2447 {
2448 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2449 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2450 eact = create_adjustment_action( "RectHeightAction",
2451 _("Height"), _("H:"), _("Height of rectangle"),
2452 "tools.shapes.rect", "height", 0,
2453 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2454 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2455 labels, values, G_N_ELEMENTS(labels),
2456 sp_rtb_height_value_changed );
2457 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2458 g_object_set_data( holder, "height_action", eact );
2459 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2460 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2461 }
2463 /* rx */
2464 {
2465 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2466 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2467 eact = create_adjustment_action( "RadiusXAction",
2468 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2469 "tools.shapes.rect", "rx", 0,
2470 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2471 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2472 labels, values, G_N_ELEMENTS(labels),
2473 sp_rtb_rx_value_changed);
2474 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2475 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2476 }
2478 /* ry */
2479 {
2480 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2481 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2482 eact = create_adjustment_action( "RadiusYAction",
2483 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2484 "tools.shapes.rect", "ry", 0,
2485 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2486 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2487 labels, values, G_N_ELEMENTS(labels),
2488 sp_rtb_ry_value_changed);
2489 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2490 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2491 }
2493 // add the units menu
2494 {
2495 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2496 gtk_action_group_add_action( mainActions, act );
2497 }
2499 /* Reset */
2500 {
2501 InkAction* inky = ink_action_new( "RectResetAction",
2502 _("Not rounded"),
2503 _("Make corners sharp"),
2504 "squared_corner",
2505 secondarySize );
2506 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2507 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2508 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2509 g_object_set_data( holder, "not_rounded", inky );
2510 }
2512 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2513 sp_rtb_sensitivize( holder );
2515 sigc::connection *connection = new sigc::connection(
2516 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2517 );
2518 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2519 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2520 }
2522 //########################
2523 //## 3D Box ##
2524 //########################
2526 // normalize angle so that it lies in the interval [0,360]
2527 static double box3d_normalize_angle (double a) {
2528 double angle = a + ((int) (a/360.0))*360;
2529 if (angle < 0) {
2530 angle += 360.0;
2531 }
2532 return angle;
2533 }
2535 static void
2536 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2537 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2538 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2539 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2540 // are reset).
2541 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2543 if (is_infinite) {
2544 gtk_toggle_action_set_active(tact, TRUE);
2545 gtk_action_set_sensitive(act, TRUE);
2547 double angle = persp3d_get_infinite_angle(persp, axis);
2548 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2549 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2550 }
2551 } else {
2552 gtk_toggle_action_set_active(tact, FALSE);
2553 gtk_action_set_sensitive(act, FALSE);
2554 }
2555 }
2557 static void
2558 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2559 if (!persp_repr) {
2560 g_print ("No perspective given to box3d_resync_toolbar().\n");
2561 return;
2562 }
2564 GtkWidget *tbl = GTK_WIDGET(data);
2565 GtkAdjustment *adj = 0;
2566 GtkAction *act = 0;
2567 GtkToggleAction *tact = 0;
2568 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2569 {
2570 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2571 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2572 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2574 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2575 }
2576 {
2577 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2578 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2579 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2581 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2582 }
2583 {
2584 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2585 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2586 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2588 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2589 }
2590 }
2592 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2593 gchar const */*old_value*/, gchar const */*new_value*/,
2594 bool /*is_interactive*/, gpointer data)
2595 {
2596 GtkWidget *tbl = GTK_WIDGET(data);
2598 // quit if run by the attr_changed listener
2599 // note: it used to work without the differently called freeze_ attributes (here and in
2600 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2601 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2602 return;
2603 }
2605 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2606 // sp_document_maybe_done() when the document is undo insensitive)
2607 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2609 // TODO: Only update the appropriate part of the toolbar
2610 // if (!strcmp(name, "inkscape:vp_z")) {
2611 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2612 // }
2614 Persp3D *persp = persp3d_get_from_repr(repr);
2615 persp3d_update_box_reprs(persp);
2617 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2618 }
2620 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2621 {
2622 NULL, /* child_added */
2623 NULL, /* child_removed */
2624 box3d_persp_tb_event_attr_changed,
2625 NULL, /* content_changed */
2626 NULL /* order_changed */
2627 };
2629 /**
2630 * \param selection Should not be NULL.
2631 */
2632 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2633 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2634 static void
2635 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2636 {
2637 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2638 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2639 // update the perspectives with infinite VPs and leave the other ones untouched).
2641 Inkscape::XML::Node *persp_repr = NULL;
2642 purge_repr_listener(tbl, tbl);
2644 SPItem *item = selection->singleItem();
2645 if (item && SP_IS_BOX3D(item)) {
2646 // FIXME: Also deal with multiple selected boxes
2647 SPBox3D *box = SP_BOX3D(item);
2648 Persp3D *persp = box3d_get_perspective(box);
2649 persp_repr = SP_OBJECT_REPR(persp);
2650 if (persp_repr) {
2651 g_object_set_data(tbl, "repr", persp_repr);
2652 Inkscape::GC::anchor(persp_repr);
2653 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2654 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2655 }
2657 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2658 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2660 box3d_resync_toolbar(persp_repr, tbl);
2661 }
2662 }
2664 static void
2665 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2666 {
2667 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2668 SPDocument *document = sp_desktop_document(desktop);
2670 // quit if run by the attr_changed listener
2671 // note: it used to work without the differently called freeze_ attributes (here and in
2672 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2673 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2674 return;
2675 }
2677 // in turn, prevent listener from responding
2678 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2680 //Persp3D *persp = document->current_persp3d;
2681 std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2682 if (sel_persps.empty()) {
2683 // this can happen when the document is created; we silently ignore it
2684 return;
2685 }
2686 Persp3D *persp = sel_persps.front();
2688 persp->tmat.set_infinite_direction (axis, adj->value);
2689 SP_OBJECT(persp)->updateRepr();
2691 // TODO: use the correct axis here, too
2692 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2694 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2695 }
2698 static void
2699 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2700 {
2701 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2702 }
2704 static void
2705 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2706 {
2707 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2708 }
2710 static void
2711 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2712 {
2713 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2714 }
2717 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2718 {
2719 // TODO: Take all selected perspectives into account
2720 std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2721 if (sel_persps.empty()) {
2722 // this can happen when the document is created; we silently ignore it
2723 return;
2724 }
2725 Persp3D *persp = sel_persps.front();
2727 bool set_infinite = gtk_toggle_action_get_active(act);
2728 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2729 }
2731 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2732 {
2733 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2734 }
2736 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2737 {
2738 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2739 }
2741 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2742 {
2743 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2744 }
2746 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2747 {
2748 EgeAdjustmentAction* eact = 0;
2749 SPDocument *document = sp_desktop_document (desktop);
2750 Persp3D *persp = document->current_persp3d;
2752 EgeAdjustmentAction* box3d_angle_x = 0;
2753 EgeAdjustmentAction* box3d_angle_y = 0;
2754 EgeAdjustmentAction* box3d_angle_z = 0;
2756 /* Angle X */
2757 {
2758 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2759 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2760 eact = create_adjustment_action( "3DBoxAngleXAction",
2761 _("Angle in X direction"), _("Angle X:"),
2762 // Translators: PL is short for 'perspective line'
2763 _("Angle of PLs in X direction"),
2764 "tools.shapes.3dbox", "box3d_angle_x", 30,
2765 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2766 -360.0, 360.0, 1.0, 10.0,
2767 labels, values, G_N_ELEMENTS(labels),
2768 box3d_angle_x_value_changed );
2769 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2770 g_object_set_data( holder, "box3d_angle_x_action", eact );
2771 box3d_angle_x = eact;
2772 }
2774 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2775 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2776 } else {
2777 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2778 }
2781 /* VP X state */
2782 {
2783 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2784 // Translators: VP is short for 'vanishing point'
2785 _("State of VP in X direction"),
2786 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2787 "toggle_vp_x",
2788 Inkscape::ICON_SIZE_DECORATION );
2789 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2790 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2791 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2792 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2793 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2794 }
2796 /* Angle Y */
2797 {
2798 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2799 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2800 eact = create_adjustment_action( "3DBoxAngleYAction",
2801 _("Angle in Y direction"), _("Angle Y:"),
2802 // Translators: PL is short for 'perspective line'
2803 _("Angle of PLs in Y direction"),
2804 "tools.shapes.3dbox", "box3d_angle_y", 30,
2805 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2806 -360.0, 360.0, 1.0, 10.0,
2807 labels, values, G_N_ELEMENTS(labels),
2808 box3d_angle_y_value_changed );
2809 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2810 g_object_set_data( holder, "box3d_angle_y_action", eact );
2811 box3d_angle_y = eact;
2812 }
2814 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2815 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2816 } else {
2817 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2818 }
2820 /* VP Y state */
2821 {
2822 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2823 // Translators: VP is short for 'vanishing point'
2824 _("State of VP in Y direction"),
2825 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2826 "toggle_vp_y",
2827 Inkscape::ICON_SIZE_DECORATION );
2828 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2829 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2830 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2831 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2832 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2833 }
2835 /* Angle Z */
2836 {
2837 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2838 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2839 eact = create_adjustment_action( "3DBoxAngleZAction",
2840 _("Angle in Z direction"), _("Angle Z:"),
2841 // Translators: PL is short for 'perspective line'
2842 _("Angle of PLs in Z direction"),
2843 "tools.shapes.3dbox", "box3d_angle_z", 30,
2844 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2845 -360.0, 360.0, 1.0, 10.0,
2846 labels, values, G_N_ELEMENTS(labels),
2847 box3d_angle_z_value_changed );
2848 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2849 g_object_set_data( holder, "box3d_angle_z_action", eact );
2850 box3d_angle_z = eact;
2851 }
2853 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2854 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2855 } else {
2856 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2857 }
2859 /* VP Z state */
2860 {
2861 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2862 // Translators: VP is short for 'vanishing point'
2863 _("State of VP in Z direction"),
2864 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2865 "toggle_vp_z",
2866 Inkscape::ICON_SIZE_DECORATION );
2867 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2868 g_object_set_data( holder, "box3d_vp_z_state_action", act );
2869 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2870 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2871 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2872 }
2874 sigc::connection *connection = new sigc::connection(
2875 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2876 );
2877 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2878 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2879 }
2881 //########################
2882 //## Spiral ##
2883 //########################
2885 static void
2886 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2887 {
2888 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2890 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2891 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2892 }
2894 // quit if run by the attr_changed listener
2895 if (g_object_get_data( tbl, "freeze" )) {
2896 return;
2897 }
2899 // in turn, prevent listener from responding
2900 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2902 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2904 bool modmade = false;
2905 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2906 items != NULL;
2907 items = items->next)
2908 {
2909 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2910 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2911 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2912 SP_OBJECT((SPItem *) items->data)->updateRepr();
2913 modmade = true;
2914 }
2915 }
2917 g_free(namespaced_name);
2919 if (modmade) {
2920 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2921 _("Change spiral"));
2922 }
2924 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2925 }
2927 static void
2928 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2929 {
2930 sp_spl_tb_value_changed(adj, tbl, "revolution");
2931 }
2933 static void
2934 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2935 {
2936 sp_spl_tb_value_changed(adj, tbl, "expansion");
2937 }
2939 static void
2940 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2941 {
2942 sp_spl_tb_value_changed(adj, tbl, "t0");
2943 }
2945 static void
2946 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2947 {
2948 GtkWidget *tbl = GTK_WIDGET(obj);
2950 GtkAdjustment *adj;
2952 // fixme: make settable
2953 gdouble rev = 5;
2954 gdouble exp = 1.0;
2955 gdouble t0 = 0.0;
2957 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2958 gtk_adjustment_set_value(adj, rev);
2959 gtk_adjustment_value_changed(adj);
2961 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2962 gtk_adjustment_set_value(adj, exp);
2963 gtk_adjustment_value_changed(adj);
2965 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2966 gtk_adjustment_set_value(adj, t0);
2967 gtk_adjustment_value_changed(adj);
2969 spinbutton_defocus(GTK_OBJECT(tbl));
2970 }
2973 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2974 gchar const */*old_value*/, gchar const */*new_value*/,
2975 bool /*is_interactive*/, gpointer data)
2976 {
2977 GtkWidget *tbl = GTK_WIDGET(data);
2979 // quit if run by the _changed callbacks
2980 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2981 return;
2982 }
2984 // in turn, prevent callbacks from responding
2985 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2987 GtkAdjustment *adj;
2988 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2989 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2991 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2992 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2994 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2995 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2997 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2998 }
3001 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3002 NULL, /* child_added */
3003 NULL, /* child_removed */
3004 spiral_tb_event_attr_changed,
3005 NULL, /* content_changed */
3006 NULL /* order_changed */
3007 };
3009 static void
3010 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3011 {
3012 int n_selected = 0;
3013 Inkscape::XML::Node *repr = NULL;
3015 purge_repr_listener( tbl, tbl );
3017 for (GSList const *items = selection->itemList();
3018 items != NULL;
3019 items = items->next)
3020 {
3021 if (SP_IS_SPIRAL((SPItem *) items->data)) {
3022 n_selected++;
3023 repr = SP_OBJECT_REPR((SPItem *) items->data);
3024 }
3025 }
3027 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3029 if (n_selected == 0) {
3030 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3031 } else if (n_selected == 1) {
3032 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3034 if (repr) {
3035 g_object_set_data( tbl, "repr", repr );
3036 Inkscape::GC::anchor(repr);
3037 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3038 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3039 }
3040 } else {
3041 // FIXME: implement averaging of all parameters for multiple selected
3042 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3043 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3044 }
3045 }
3048 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3049 {
3050 EgeAdjustmentAction* eact = 0;
3051 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3053 {
3054 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3055 ege_output_action_set_use_markup( act, TRUE );
3056 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3057 g_object_set_data( holder, "mode_action", act );
3058 }
3060 /* Revolution */
3061 {
3062 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3063 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3064 eact = create_adjustment_action( "SpiralRevolutionAction",
3065 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3066 "tools.shapes.spiral", "revolution", 3.0,
3067 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3068 0.01, 1024.0, 0.1, 1.0,
3069 labels, values, G_N_ELEMENTS(labels),
3070 sp_spl_tb_revolution_value_changed, 1, 2);
3071 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3072 }
3074 /* Expansion */
3075 {
3076 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3077 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3078 eact = create_adjustment_action( "SpiralExpansionAction",
3079 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3080 "tools.shapes.spiral", "expansion", 1.0,
3081 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3082 0.0, 1000.0, 0.01, 1.0,
3083 labels, values, G_N_ELEMENTS(labels),
3084 sp_spl_tb_expansion_value_changed);
3085 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3086 }
3088 /* T0 */
3089 {
3090 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3091 gdouble values[] = {0, 0.5, 0.9};
3092 eact = create_adjustment_action( "SpiralT0Action",
3093 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3094 "tools.shapes.spiral", "t0", 0.0,
3095 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3096 0.0, 0.999, 0.01, 1.0,
3097 labels, values, G_N_ELEMENTS(labels),
3098 sp_spl_tb_t0_value_changed);
3099 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3100 }
3102 /* Reset */
3103 {
3104 InkAction* inky = ink_action_new( "SpiralResetAction",
3105 _("Defaults"),
3106 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3107 GTK_STOCK_CLEAR,
3108 secondarySize );
3109 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3110 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3111 }
3114 sigc::connection *connection = new sigc::connection(
3115 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3116 );
3117 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3118 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3119 }
3121 //########################
3122 //## Pen/Pencil ##
3123 //########################
3125 static void sp_pc_spiro_spline_mode_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
3126 {
3127 prefs_set_int_attribute("tools.freehand", "spiro-spline-mode", ege_select_one_action_get_active(act));
3128 }
3130 static void sp_add_spiro_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3131 {
3132 // FIXME: No action is needed, we only want a simple label. But sp_toolbox_add_label() always
3133 // adds the label at the end of the toolbar, whence this workarund. How to avoid this?
3134 {
3135 EgeOutputAction* act = ege_output_action_new(
3136 tool_is_pencil ?
3137 "FreehandModeActionPencilTemp" :
3138 "FreehandModeActionPenTemp",
3139 _("<b>Mode:</b>"), "", 0 );
3140 ege_output_action_set_use_markup( act, TRUE );
3141 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3142 g_object_set_data( holder, "freehand_mode_action", act );
3143 }
3145 /* Freehand mode toggle buttons */
3146 {
3147 //gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
3148 //bool isSpiroMode = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
3149 guint spiroMode = prefs_get_int_attribute("tools.freehand", "spiro-spline-mode", 0);
3150 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3152 {
3153 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3155 GtkTreeIter iter;
3156 gtk_list_store_append( model, &iter );
3157 gtk_list_store_set( model, &iter,
3158 0, _("Bézier"),
3159 1, _("Regular Bézier mode"),
3160 2, "bezier_mode",
3161 -1 );
3163 gtk_list_store_append( model, &iter );
3164 gtk_list_store_set( model, &iter,
3165 0, _("Spiro"),
3166 1, _("Spiro splines mode"),
3167 2, "spiro_splines_mode",
3168 -1 );
3170 EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3171 "FreehandModeActionPencil" :
3172 "FreehandModeActionPen",
3173 (""), (""), NULL, GTK_TREE_MODEL(model) );
3174 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3175 g_object_set_data( holder, "freehande_mode_action", act );
3177 ege_select_one_action_set_appearance( act, "full" );
3178 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3179 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3180 ege_select_one_action_set_icon_column( act, 2 );
3181 ege_select_one_action_set_icon_size( act, secondarySize );
3182 ege_select_one_action_set_tooltip_column( act, 1 );
3184 ege_select_one_action_set_active( act, spiroMode);
3185 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_pc_spiro_spline_mode_changed), holder);
3186 }
3187 }
3188 }
3190 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3191 {
3192 sp_add_spiro_toggle(mainActions, holder, false);
3193 }
3196 static void
3197 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3198 {
3199 GtkWidget *tbl = GTK_WIDGET(obj);
3201 GtkAdjustment *adj;
3203 // fixme: make settable
3204 gdouble tolerance = 4;
3206 adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3207 gtk_adjustment_set_value(adj, tolerance);
3208 gtk_adjustment_value_changed(adj);
3210 spinbutton_defocus(GTK_OBJECT(tbl));
3211 }
3213 static void
3214 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3215 {
3217 // quit if run by the attr_changed listener
3218 if (g_object_get_data( tbl, "freeze" )) {
3219 return;
3220 }
3221 // in turn, prevent listener from responding
3222 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3223 prefs_set_double_attribute("tools.freehand.pencil",
3224 "tolerance", adj->value);
3225 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3227 }
3231 static void
3232 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node *repr,
3233 const gchar *key,
3234 const gchar *oldval,
3235 const gchar *newval,
3236 bool is_interactive,
3237 void * data)
3238 {
3239 GObject* tbl = G_OBJECT(data);
3240 if (g_object_get_data( tbl, "freeze" )) {
3241 return;
3242 }
3244 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3246 GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3247 "tolerance");
3249 double v = prefs_get_double_attribute("tools.freehand.pencil",
3250 "tolerance", adj->value);
3251 gtk_adjustment_set_value(adj, v);
3252 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3254 }
3256 static Inkscape::XML::NodeEventVector pencil_node_events =
3257 {
3258 NULL,
3259 NULL,
3260 sp_pencil_tb_tolerance_value_changed_external,
3261 NULL,
3262 NULL,
3263 };
3266 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3267 {
3268 sp_add_spiro_toggle(mainActions, holder, true);
3270 EgeAdjustmentAction* eact = 0;
3272 /* Tolerance */
3273 {
3275 eact = create_adjustment_action( "PencilToleranceAction",
3276 _("Number of pixels allowed in interpolating"),
3277 _("Tolerance:"), _("Tolerance"),
3278 "tools.freehand.pencil", "tolerance",
3279 3.0,
3280 GTK_WIDGET(desktop->canvas), NULL,
3281 holder, TRUE, "altx-pencil",
3282 0.5, 100.0, 0.5, 1.0,
3283 NULL, NULL, 0,
3284 sp_pencil_tb_tolerance_value_changed,
3285 1, 2);
3286 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3288 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3289 "tools.freehand.pencil");
3290 repr->addListener(&pencil_node_events, G_OBJECT(holder));
3291 g_object_set_data(G_OBJECT(holder), "repr", repr);
3293 }
3294 /* Reset */
3295 {
3296 InkAction* inky = ink_action_new( "PencilResetAction",
3297 _("Defaults"),
3298 _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3299 GTK_STOCK_CLEAR,
3300 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3301 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3302 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3303 }
3305 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3307 }
3310 //########################
3311 //## Tweak ##
3312 //########################
3314 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3315 {
3316 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3317 }
3319 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3320 {
3321 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3322 }
3324 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3325 {
3326 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3327 }
3329 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3330 {
3331 int mode = ege_select_one_action_get_active( act );
3332 prefs_set_int_attribute("tools.tweak", "mode", mode);
3334 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3335 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3336 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3337 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3338 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3339 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3340 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3341 if (doh) gtk_action_set_sensitive (doh, TRUE);
3342 if (dos) gtk_action_set_sensitive (dos, TRUE);
3343 if (dol) gtk_action_set_sensitive (dol, TRUE);
3344 if (doo) gtk_action_set_sensitive (doo, TRUE);
3345 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3346 if (fid) gtk_action_set_sensitive (fid, FALSE);
3347 } else {
3348 if (doh) gtk_action_set_sensitive (doh, FALSE);
3349 if (dos) gtk_action_set_sensitive (dos, FALSE);
3350 if (dol) gtk_action_set_sensitive (dol, FALSE);
3351 if (doo) gtk_action_set_sensitive (doo, FALSE);
3352 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3353 if (fid) gtk_action_set_sensitive (fid, TRUE);
3354 }
3355 }
3357 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3358 {
3359 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3360 }
3362 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3363 bool show = gtk_toggle_action_get_active( act );
3364 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3365 }
3366 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3367 bool show = gtk_toggle_action_get_active( act );
3368 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3369 }
3370 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3371 bool show = gtk_toggle_action_get_active( act );
3372 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3373 }
3374 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3375 bool show = gtk_toggle_action_get_active( act );
3376 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3377 }
3379 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3380 {
3381 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3383 {
3384 /* Width */
3385 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3386 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3387 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3388 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3389 "tools.tweak", "width", 15,
3390 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3391 1, 100, 1.0, 10.0,
3392 labels, values, G_N_ELEMENTS(labels),
3393 sp_tweak_width_value_changed, 0.01, 0, 100 );
3394 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3395 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3396 }
3399 {
3400 /* Force */
3401 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3402 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3403 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3404 _("Force"), _("Force:"), _("The force of the tweak action"),
3405 "tools.tweak", "force", 20,
3406 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3407 1, 100, 1.0, 10.0,
3408 labels, values, G_N_ELEMENTS(labels),
3409 sp_tweak_force_value_changed, 0.01, 0, 100 );
3410 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3411 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3412 }
3414 /* Mode */
3415 {
3416 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3418 GtkTreeIter iter;
3419 gtk_list_store_append( model, &iter );
3420 gtk_list_store_set( model, &iter,
3421 0, _("Push mode"),
3422 1, _("Push parts of paths in any direction"),
3423 2, "tweak_push_mode",
3424 -1 );
3426 gtk_list_store_append( model, &iter );
3427 gtk_list_store_set( model, &iter,
3428 0, _("Shrink mode"),
3429 1, _("Shrink (inset) parts of paths"),
3430 2, "tweak_shrink_mode",
3431 -1 );
3433 gtk_list_store_append( model, &iter );
3434 gtk_list_store_set( model, &iter,
3435 0, _("Grow mode"),
3436 1, _("Grow (outset) parts of paths"),
3437 2, "tweak_grow_mode",
3438 -1 );
3440 gtk_list_store_append( model, &iter );
3441 gtk_list_store_set( model, &iter,
3442 0, _("Attract mode"),
3443 1, _("Attract parts of paths towards cursor"),
3444 2, "tweak_attract_mode",
3445 -1 );
3447 gtk_list_store_append( model, &iter );
3448 gtk_list_store_set( model, &iter,
3449 0, _("Repel mode"),
3450 1, _("Repel parts of paths from cursor"),
3451 2, "tweak_repel_mode",
3452 -1 );
3454 gtk_list_store_append( model, &iter );
3455 gtk_list_store_set( model, &iter,
3456 0, _("Roughen mode"),
3457 1, _("Roughen parts of paths"),
3458 2, "tweak_roughen_mode",
3459 -1 );
3461 gtk_list_store_append( model, &iter );
3462 gtk_list_store_set( model, &iter,
3463 0, _("Color paint mode"),
3464 1, _("Paint the tool's color upon selected objects"),
3465 2, "tweak_colorpaint_mode",
3466 -1 );
3468 gtk_list_store_append( model, &iter );
3469 gtk_list_store_set( model, &iter,
3470 0, _("Color jitter mode"),
3471 1, _("Jitter the colors of selected objects"),
3472 2, "tweak_colorjitter_mode",
3473 -1 );
3475 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3476 g_object_set( act, "short_label", _("Mode:"), NULL );
3477 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3478 g_object_set_data( holder, "mode_action", act );
3480 ege_select_one_action_set_appearance( act, "full" );
3481 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3482 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3483 ege_select_one_action_set_icon_column( act, 2 );
3484 ege_select_one_action_set_icon_size( act, secondarySize );
3485 ege_select_one_action_set_tooltip_column( act, 1 );
3487 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3488 ege_select_one_action_set_active( act, mode );
3489 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3491 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3492 }
3494 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3496 {
3497 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3498 ege_output_action_set_use_markup( act, TRUE );
3499 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3500 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3501 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3502 g_object_set_data( holder, "tweak_channels_label", act);
3503 }
3505 {
3506 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3507 _("Hue"),
3508 _("In color mode, act on objects' hue"),
3509 NULL,
3510 Inkscape::ICON_SIZE_DECORATION );
3511 //TRANSLATORS: "H" here stands for hue
3512 g_object_set( act, "short_label", _("H"), NULL );
3513 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3514 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3515 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3516 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3517 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3518 g_object_set_data( holder, "tweak_doh", act);
3519 }
3520 {
3521 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3522 _("Saturation"),
3523 _("In color mode, act on objects' saturation"),
3524 NULL,
3525 Inkscape::ICON_SIZE_DECORATION );
3526 //TRANSLATORS: "S" here stands for Saturation
3527 g_object_set( act, "short_label", _("S"), NULL );
3528 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3529 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3530 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3531 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3532 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3533 g_object_set_data( holder, "tweak_dos", act );
3534 }
3535 {
3536 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3537 _("Lightness"),
3538 _("In color mode, act on objects' lightness"),
3539 NULL,
3540 Inkscape::ICON_SIZE_DECORATION );
3541 //TRANSLATORS: "L" here stands for Lightness
3542 g_object_set( act, "short_label", _("L"), NULL );
3543 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3544 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3545 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3546 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3547 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3548 g_object_set_data( holder, "tweak_dol", act );
3549 }
3550 {
3551 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3552 _("Opacity"),
3553 _("In color mode, act on objects' opacity"),
3554 NULL,
3555 Inkscape::ICON_SIZE_DECORATION );
3556 //TRANSLATORS: "O" here stands for Opacity
3557 g_object_set( act, "short_label", _("O"), NULL );
3558 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3559 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3560 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3561 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3562 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3563 g_object_set_data( holder, "tweak_doo", act );
3564 }
3566 { /* Fidelity */
3567 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3568 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3569 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3570 _("Fidelity"), _("Fidelity:"),
3571 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3572 "tools.tweak", "fidelity", 50,
3573 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3574 1, 100, 1.0, 10.0,
3575 labels, values, G_N_ELEMENTS(labels),
3576 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3577 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3578 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3579 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3580 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3581 g_object_set_data( holder, "tweak_fidelity", eact );
3582 }
3585 /* Use Pressure button */
3586 {
3587 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3588 _("Pressure"),
3589 _("Use the pressure of the input device to alter the force of tweak action"),
3590 "use_pressure",
3591 Inkscape::ICON_SIZE_DECORATION );
3592 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3593 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3594 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3595 }
3597 }
3600 //########################
3601 //## Calligraphy ##
3602 //########################
3603 static void update_presets_list(GObject *dataKludge ){
3604 EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3605 if (sel) {
3606 ege_select_one_action_set_active(sel, 0 );
3607 }
3608 }
3610 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3611 {
3612 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3613 update_presets_list(tbl);
3614 }
3616 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3617 {
3618 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3619 update_presets_list(tbl);
3620 }
3622 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3623 {
3624 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3625 update_presets_list(tbl);
3626 }
3628 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3629 {
3630 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3631 update_presets_list(tbl);
3632 }
3634 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3635 {
3636 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3637 update_presets_list(tbl);
3638 }
3640 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3641 {
3642 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3643 update_presets_list(tbl);
3644 }
3646 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3647 {
3648 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3649 update_presets_list(tbl);
3650 }
3652 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3653 {
3654 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3655 update_presets_list(tbl);
3656 }
3658 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject* tbl )
3659 {
3660 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3661 update_presets_list(tbl);
3662 }
3664 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject* tbl )
3665 {
3666 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3667 update_presets_list(tbl);
3668 }
3670 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject* tbl )
3671 {
3672 GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle"));
3673 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3674 update_presets_list(tbl);
3675 if (calligraphy_angle )
3676 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3677 }
3680 #define PROFILE_FLOAT_SIZE 7
3681 #define PROFILE_INT_SIZE 4
3682 struct ProfileFloatElement {
3683 char const *name;
3684 double def;
3685 double min;
3686 double max;
3687 };
3688 struct ProfileIntElement {
3689 char const *name;
3690 int def;
3691 int min;
3692 int max;
3693 };
3697 static ProfileFloatElement f_profile[PROFILE_FLOAT_SIZE] = {
3698 {"mass",0.02, 0.0, 1.0},
3699 {"wiggle",0.0, 0.0, 1.0},
3700 {"angle",30.0, -90.0, 90.0},
3701 {"thinning",0.1, -1.0, 1.0},
3702 {"tremor",0.0, 0.0, 1.0},
3703 {"flatness",0.9, 0.0, 1.0},
3704 {"cap_rounding",0.0, 0.0, 5.0}
3705 };
3706 static ProfileIntElement i_profile[PROFILE_INT_SIZE] = {
3707 {"width",15, 1, 100},
3708 {"usepressure",1,0,1},
3709 {"tracebackground",0,0,1},
3710 {"usetilt",1,0,1},
3711 };
3715 static void sp_dcc_save_profile( GtkWidget */*widget*/, GObject *dataKludge ){
3716 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3717 if (! desktop) return;
3719 Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
3720 if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) return;
3721 Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
3723 unsigned int new_index = pref_path_number_of_children("tools.calligraphic.preset") +1;
3724 gchar *profile_id = g_strdup_printf("dcc%d", new_index);
3725 gchar *pref_path = create_pref("tools.calligraphic.preset",profile_id);
3727 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3728 ProfileFloatElement const &pe = f_profile[i];
3729 double v = prefs_get_double_attribute_limited("tools.calligraphic",pe.name, pe.def, pe.min, pe.max);
3730 prefs_set_double_attribute(pref_path,pe.name,v);
3731 }
3732 for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3733 ProfileIntElement const &pe = i_profile[i];
3734 int v = prefs_get_int_attribute_limited("tools.calligraphic",pe.name, pe.def,pe.min, pe.max);
3735 prefs_set_int_attribute(pref_path,pe.name,v);
3736 }
3737 prefs_set_string_attribute(pref_path,"name",profile_name.c_str());
3739 EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(dataKludge, "profile_selector"));
3740 GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
3741 GtkTreeIter iter;
3742 gtk_list_store_append( model, &iter );
3743 gtk_list_store_set( model, &iter, 0, profile_name.c_str(), 1, new_index, -1 );
3745 free(profile_id);
3746 free(pref_path);
3748 ege_select_one_action_set_active(selector, new_index);
3749 }
3752 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3754 gint preset_index = ege_select_one_action_get_active( act );
3755 gchar *profile_name = get_pref_nth_child("tools.calligraphic.preset", preset_index);
3757 if ( profile_name) {
3758 g_object_set_data(dataKludge, "profile_selector",NULL); //temporary hides the selector so no one will updadte it
3759 for (unsigned i = 0; i < PROFILE_FLOAT_SIZE; ++i) {
3760 ProfileFloatElement const &pe = f_profile[i];
3761 double v = prefs_get_double_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3762 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3763 if ( adj ) {
3764 gtk_adjustment_set_value(adj, v);
3765 }
3766 }
3767 for (unsigned i = 0; i < PROFILE_INT_SIZE; ++i) {
3768 ProfileIntElement const &pe = i_profile[i];
3769 int v = prefs_get_int_attribute_limited(profile_name, pe.name, pe.def, pe.min, pe.max);
3770 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(g_object_get_data(dataKludge, pe.name));
3771 if ( toggle ) {
3772 gtk_toggle_action_set_active(toggle, v);
3773 } else printf("No toggle");
3774 }
3775 free(profile_name);
3776 g_object_set_data(dataKludge, "profile_selector",act); //restore selector visibility
3777 }
3779 }
3782 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3783 {
3784 {
3785 EgeAdjustmentAction* calligraphy_angle = 0;
3787 {
3788 /* Width */
3789 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3790 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3791 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3792 _("Pen Width"), _("Width:"),
3793 _("The width of the calligraphic pen (relative to the visible canvas area)"),
3794 "tools.calligraphic", "width", 15,
3795 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3796 1, 100, 1.0, 10.0,
3797 labels, values, G_N_ELEMENTS(labels),
3798 sp_ddc_width_value_changed, 0.01, 0, 100 );
3799 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3800 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3801 }
3803 {
3804 /* Thinning */
3805 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3806 gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3807 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3808 _("Stroke Thinning"), _("Thinning:"),
3809 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3810 "tools.calligraphic", "thinning", 0.1,
3811 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3812 -1.0, 1.0, 0.01, 0.1,
3813 labels, values, G_N_ELEMENTS(labels),
3814 sp_ddc_velthin_value_changed, 0.01, 2);
3815 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3816 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3817 }
3819 {
3820 /* Angle */
3821 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3822 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3823 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3824 _("Pen Angle"), _("Angle:"),
3825 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3826 "tools.calligraphic", "angle", 30,
3827 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3828 -90.0, 90.0, 1.0, 10.0,
3829 labels, values, G_N_ELEMENTS(labels),
3830 sp_ddc_angle_value_changed, 1, 0 );
3831 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3832 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3833 g_object_set_data( holder, "angle", eact );
3834 calligraphy_angle = eact;
3835 }
3837 {
3838 /* Fixation */
3839 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3840 gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3841 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3842 _("Fixation"), _("Fixation:"),
3843 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3844 "tools.calligraphic", "flatness", 0.9,
3845 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3846 0.0, 1.0, 0.01, 0.1,
3847 labels, values, G_N_ELEMENTS(labels),
3848 sp_ddc_flatness_value_changed, 0.01, 2 );
3849 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3850 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3851 }
3853 {
3854 /* Cap Rounding */
3855 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3856 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3857 // TRANSLATORS: "cap" means "end" (both start and finish) here
3858 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3859 _("Cap rounding"), _("Caps:"),
3860 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3861 "tools.calligraphic", "cap_rounding", 0.0,
3862 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3863 0.0, 5.0, 0.01, 0.1,
3864 labels, values, G_N_ELEMENTS(labels),
3865 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3866 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3867 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3868 }
3870 {
3871 /* Tremor */
3872 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3873 gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3874 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3875 _("Stroke Tremor"), _("Tremor:"),
3876 _("Increase to make strokes rugged and trembling"),
3877 "tools.calligraphic", "tremor", 0.0,
3878 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3879 0.0, 1.0, 0.01, 0.1,
3880 labels, values, G_N_ELEMENTS(labels),
3881 sp_ddc_tremor_value_changed, 0.01, 2 );
3883 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3884 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3885 }
3887 {
3888 /* Wiggle */
3889 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3890 gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3891 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3892 _("Pen Wiggle"), _("Wiggle:"),
3893 _("Increase to make the pen waver and wiggle"),
3894 "tools.calligraphic", "wiggle", 0.0,
3895 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3896 0.0, 1.0, 0.01, 0.1,
3897 labels, values, G_N_ELEMENTS(labels),
3898 sp_ddc_wiggle_value_changed, 0.01, 2 );
3899 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3900 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3901 }
3903 {
3904 /* Mass */
3905 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3906 gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3907 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3908 _("Pen Mass"), _("Mass:"),
3909 _("Increase to make the pen drag behind, as if slowed by inertia"),
3910 "tools.calligraphic", "mass", 0.02,
3911 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3912 0.0, 1.0, 0.01, 0.1,
3913 labels, values, G_N_ELEMENTS(labels),
3914 sp_ddc_mass_value_changed, 0.01, 2 );
3915 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3916 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3917 }
3920 /* Trace Background button */
3921 {
3922 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3923 _("Trace Background"),
3924 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3925 "trace_background",
3926 Inkscape::ICON_SIZE_DECORATION );
3927 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3928 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
3929 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3930 g_object_set_data( holder, "tracebackground", act );
3931 }
3933 /* Use Pressure button */
3934 {
3935 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3936 _("Pressure"),
3937 _("Use the pressure of the input device to alter the width of the pen"),
3938 "use_pressure",
3939 Inkscape::ICON_SIZE_DECORATION );
3940 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3941 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
3942 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3943 g_object_set_data( holder, "usepressure", act );
3944 }
3946 /* Use Tilt button */
3947 {
3948 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3949 _("Tilt"),
3950 _("Use the tilt of the input device to alter the angle of the pen's nib"),
3951 "use_tilt",
3952 Inkscape::ICON_SIZE_DECORATION );
3953 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3954 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
3955 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3956 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3957 g_object_set_data( holder, "usetilt", act );
3958 }
3960 /*calligraphic profile */
3961 {
3962 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3963 gchar *pref_path;
3966 GtkTreeIter iter;
3967 gtk_list_store_append( model, &iter );
3968 gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
3970 //TODO: switch back to prefs API
3971 Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
3972 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
3973 int ii=1;
3974 while (child_repr) {
3975 GtkTreeIter iter;
3976 char *preset_name = (char *) child_repr->attribute("name");
3977 gtk_list_store_append( model, &iter );
3978 gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
3979 child_repr = sp_repr_next(child_repr);
3980 }
3982 pref_path = NULL;
3983 EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", "" , (_("Change calligraphic profile")), NULL, GTK_TREE_MODEL(model) );
3984 ege_select_one_action_set_appearance( act1, "compact" );
3985 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
3986 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3987 g_object_set_data( holder, "profile_selector", act1 );
3989 }
3991 /*Save or delete calligraphic profile */
3992 {
3993 GtkAction* act = gtk_action_new( "SaveDeleteProfileAction",
3994 _("Defaults"),
3995 _("Save current settings as new profile"),
3996 GTK_STOCK_SAVE );
3997 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_dcc_save_profile), holder );
4000 gtk_action_group_add_action( mainActions, act );
4001 gtk_action_set_sensitive( act, TRUE );
4002 g_object_set_data( holder, "profile_save_delete", act );
4003 }
4004 }
4005 }
4008 //########################
4009 //## Circle / Arc ##
4010 //########################
4012 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4013 {
4014 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4015 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4017 if (v1 == 0 && v2 == 0) {
4018 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4019 gtk_action_set_sensitive( ocb, FALSE );
4020 gtk_action_set_sensitive( make_whole, FALSE );
4021 }
4022 } else {
4023 gtk_action_set_sensitive( ocb, TRUE );
4024 gtk_action_set_sensitive( make_whole, TRUE );
4025 }
4026 }
4028 static void
4029 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4030 {
4031 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4033 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4034 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4035 }
4037 // quit if run by the attr_changed listener
4038 if (g_object_get_data( tbl, "freeze" )) {
4039 return;
4040 }
4042 // in turn, prevent listener from responding
4043 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4045 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4047 bool modmade = false;
4048 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4049 items != NULL;
4050 items = items->next)
4051 {
4052 SPItem *item = SP_ITEM(items->data);
4054 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4056 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4057 SPArc *arc = SP_ARC(item);
4059 if (!strcmp(value_name, "start"))
4060 ge->start = (adj->value * M_PI)/ 180;
4061 else
4062 ge->end = (adj->value * M_PI)/ 180;
4064 sp_genericellipse_normalize(ge);
4065 ((SPObject *)arc)->updateRepr();
4066 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4068 modmade = true;
4069 }
4070 }
4072 g_free(namespaced_name);
4074 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4076 sp_arctb_sensitivize( tbl, adj->value, other->value );
4078 if (modmade) {
4079 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4080 _("Arc: Change start/end"));
4081 }
4083 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4084 }
4087 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
4088 {
4089 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
4090 }
4092 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4093 {
4094 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
4095 }
4098 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4099 {
4100 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4101 gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
4102 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4103 prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
4104 }
4106 // only take action if run by the attr_changed listener
4107 if (!g_object_get_data( tbl, "freeze" )) {
4108 // in turn, prevent listener from responding
4109 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4111 if ( eraserMode != 0 ) {
4112 } else {
4113 }
4114 // TODO finish implementation
4116 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4117 }
4118 }
4120 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4121 {
4122 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4123 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4124 if ( ege_select_one_action_get_active( act ) != 0 ) {
4125 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4126 } else {
4127 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4128 }
4129 }
4131 // quit if run by the attr_changed listener
4132 if (g_object_get_data( tbl, "freeze" )) {
4133 return;
4134 }
4136 // in turn, prevent listener from responding
4137 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4139 bool modmade = false;
4141 if ( ege_select_one_action_get_active(act) != 0 ) {
4142 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4143 items != NULL;
4144 items = items->next)
4145 {
4146 if (SP_IS_ARC((SPItem *) items->data)) {
4147 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4148 repr->setAttribute("sodipodi:open", "true");
4149 SP_OBJECT((SPItem *) items->data)->updateRepr();
4150 modmade = true;
4151 }
4152 }
4153 } else {
4154 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4155 items != NULL;
4156 items = items->next)
4157 {
4158 if (SP_IS_ARC((SPItem *) items->data)) {
4159 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4160 repr->setAttribute("sodipodi:open", NULL);
4161 SP_OBJECT((SPItem *) items->data)->updateRepr();
4162 modmade = true;
4163 }
4164 }
4165 }
4167 if (modmade) {
4168 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4169 _("Arc: Change open/closed"));
4170 }
4172 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4173 }
4175 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4176 {
4177 GtkAdjustment *adj;
4178 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4179 gtk_adjustment_set_value(adj, 0.0);
4180 gtk_adjustment_value_changed(adj);
4182 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4183 gtk_adjustment_set_value(adj, 0.0);
4184 gtk_adjustment_value_changed(adj);
4186 spinbutton_defocus( GTK_OBJECT(obj) );
4187 }
4189 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4190 gchar const */*old_value*/, gchar const */*new_value*/,
4191 bool /*is_interactive*/, gpointer data)
4192 {
4193 GObject *tbl = G_OBJECT(data);
4195 // quit if run by the _changed callbacks
4196 if (g_object_get_data( tbl, "freeze" )) {
4197 return;
4198 }
4200 // in turn, prevent callbacks from responding
4201 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4203 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4204 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4206 GtkAdjustment *adj1,*adj2;
4207 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4208 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4209 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4210 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4212 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4214 char const *openstr = NULL;
4215 openstr = repr->attribute("sodipodi:open");
4216 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4218 if (openstr) {
4219 ege_select_one_action_set_active( ocb, 1 );
4220 } else {
4221 ege_select_one_action_set_active( ocb, 0 );
4222 }
4224 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4225 }
4227 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4228 NULL, /* child_added */
4229 NULL, /* child_removed */
4230 arc_tb_event_attr_changed,
4231 NULL, /* content_changed */
4232 NULL /* order_changed */
4233 };
4236 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4237 {
4238 int n_selected = 0;
4239 Inkscape::XML::Node *repr = NULL;
4241 purge_repr_listener( tbl, tbl );
4243 for (GSList const *items = selection->itemList();
4244 items != NULL;
4245 items = items->next)
4246 {
4247 if (SP_IS_ARC((SPItem *) items->data)) {
4248 n_selected++;
4249 repr = SP_OBJECT_REPR((SPItem *) items->data);
4250 }
4251 }
4253 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4255 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4256 if (n_selected == 0) {
4257 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4258 } else if (n_selected == 1) {
4259 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4260 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4262 if (repr) {
4263 g_object_set_data( tbl, "repr", repr );
4264 Inkscape::GC::anchor(repr);
4265 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4266 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4267 }
4268 } else {
4269 // FIXME: implement averaging of all parameters for multiple selected
4270 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4271 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4272 sp_arctb_sensitivize( tbl, 1, 0 );
4273 }
4274 }
4277 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4278 {
4279 EgeAdjustmentAction* eact = 0;
4280 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4283 {
4284 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4285 ege_output_action_set_use_markup( act, TRUE );
4286 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4287 g_object_set_data( holder, "mode_action", act );
4288 }
4290 /* Start */
4291 {
4292 eact = create_adjustment_action( "ArcStartAction",
4293 _("Start"), _("Start:"),
4294 _("The angle (in degrees) from the horizontal to the arc's start point"),
4295 "tools.shapes.arc", "start", 0.0,
4296 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4297 -360.0, 360.0, 1.0, 10.0,
4298 0, 0, 0,
4299 sp_arctb_start_value_changed);
4300 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4301 }
4303 /* End */
4304 {
4305 eact = create_adjustment_action( "ArcEndAction",
4306 _("End"), _("End:"),
4307 _("The angle (in degrees) from the horizontal to the arc's end point"),
4308 "tools.shapes.arc", "end", 0.0,
4309 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4310 -360.0, 360.0, 1.0, 10.0,
4311 0, 0, 0,
4312 sp_arctb_end_value_changed);
4313 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4314 }
4316 /* Segments / Pie checkbox */
4317 {
4318 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4320 GtkTreeIter iter;
4321 gtk_list_store_append( model, &iter );
4322 gtk_list_store_set( model, &iter,
4323 0, _("Closed arc"),
4324 1, _("Switch to segment (closed shape with two radii)"),
4325 2, "circle_closed_arc",
4326 -1 );
4328 gtk_list_store_append( model, &iter );
4329 gtk_list_store_set( model, &iter,
4330 0, _("Open Arc"),
4331 1, _("Switch to arc (unclosed shape)"),
4332 2, "circle_open_arc",
4333 -1 );
4335 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4336 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4337 g_object_set_data( holder, "open_action", act );
4339 ege_select_one_action_set_appearance( act, "full" );
4340 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4341 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4342 ege_select_one_action_set_icon_column( act, 2 );
4343 ege_select_one_action_set_icon_size( act, secondarySize );
4344 ege_select_one_action_set_tooltip_column( act, 1 );
4346 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4347 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4348 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4349 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4350 }
4352 /* Make Whole */
4353 {
4354 InkAction* inky = ink_action_new( "ArcResetAction",
4355 _("Make whole"),
4356 _("Make the shape a whole ellipse, not arc or segment"),
4357 "reset_circle",
4358 secondarySize );
4359 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4360 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4361 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4362 g_object_set_data( holder, "make_whole", inky );
4363 }
4365 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4366 // sensitivize make whole and open checkbox
4367 {
4368 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4369 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4370 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4371 }
4374 sigc::connection *connection = new sigc::connection(
4375 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4376 );
4377 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4378 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4379 }
4384 // toggle button callbacks and updaters
4386 //########################
4387 //## Dropper ##
4388 //########################
4390 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4391 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4392 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4393 if ( set_action ) {
4394 if ( gtk_toggle_action_get_active( act ) ) {
4395 gtk_action_set_sensitive( set_action, TRUE );
4396 } else {
4397 gtk_action_set_sensitive( set_action, FALSE );
4398 }
4399 }
4401 spinbutton_defocus(GTK_OBJECT(tbl));
4402 }
4404 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4405 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4406 spinbutton_defocus(GTK_OBJECT(tbl));
4407 }
4410 /**
4411 * Dropper auxiliary toolbar construction and setup.
4412 *
4413 * TODO: Would like to add swatch of current color.
4414 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4415 * can drag and drop places. Will provide a nice mixing palette.
4416 */
4417 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4418 {
4419 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4421 {
4422 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4423 ege_output_action_set_use_markup( act, TRUE );
4424 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4425 }
4427 {
4428 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4429 _("Pick opacity"),
4430 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4431 NULL,
4432 Inkscape::ICON_SIZE_DECORATION );
4433 g_object_set( act, "short_label", _("Pick"), NULL );
4434 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4435 g_object_set_data( holder, "pick_action", act );
4436 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4437 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4438 }
4440 {
4441 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4442 _("Assign opacity"),
4443 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4444 NULL,
4445 Inkscape::ICON_SIZE_DECORATION );
4446 g_object_set( act, "short_label", _("Assign"), NULL );
4447 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4448 g_object_set_data( holder, "set_action", act );
4449 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4450 // make sure it's disabled if we're not picking alpha
4451 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4452 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4453 }
4454 }
4458 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4459 {
4460 {
4461 /* Width */
4462 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4463 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4464 EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
4465 _("Pen Width"), _("Width:"),
4466 _("The width of the eraser pen (relative to the visible canvas area)"),
4467 "tools.eraser", "width", 15,
4468 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
4469 1, 100, 1.0, 10.0,
4470 labels, values, G_N_ELEMENTS(labels),
4471 sp_ddc_width_value_changed, 0.01, 0, 100 );
4472 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4473 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4474 }
4476 {
4477 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4479 GtkTreeIter iter;
4480 gtk_list_store_append( model, &iter );
4481 gtk_list_store_set( model, &iter,
4482 0, _("Delete"),
4483 1, _("Delete objects touched by the eraser"),
4484 2, "delete_object",
4485 -1 );
4487 gtk_list_store_append( model, &iter );
4488 gtk_list_store_set( model, &iter,
4489 0, _("Cut"),
4490 1, _("Cut out from objects"),
4491 2, "difference",
4492 -1 );
4494 EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4495 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4496 g_object_set_data( holder, "eraser_mode_action", act );
4498 ege_select_one_action_set_appearance( act, "full" );
4499 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4500 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4501 ege_select_one_action_set_icon_column( act, 2 );
4502 ege_select_one_action_set_tooltip_column( act, 1 );
4504 gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
4505 ege_select_one_action_set_active( act, eraserMode );
4506 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
4507 }
4509 }
4511 //########################
4512 //## Text Toolbox ##
4513 //########################
4514 /*
4515 static void
4516 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4517 {
4518 //Call back for letter sizing spinbutton
4519 }
4521 static void
4522 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4523 {
4524 //Call back for line height spinbutton
4525 }
4527 static void
4528 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4529 {
4530 //Call back for horizontal kerning spinbutton
4531 }
4533 static void
4534 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4535 {
4536 //Call back for vertical kerning spinbutton
4537 }
4539 static void
4540 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4541 {
4542 //Call back for letter rotation spinbutton
4543 }*/
4545 namespace {
4547 bool popdown_visible = false;
4548 bool popdown_hasfocus = false;
4550 void
4551 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4552 {
4553 SPStyle *query =
4554 sp_style_new (SP_ACTIVE_DOCUMENT);
4556 int result_fontspec =
4557 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4559 int result_family =
4560 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4562 int result_style =
4563 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4565 int result_numbers =
4566 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4568 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4570 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4571 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4572 {
4573 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4575 if (repr)
4576 {
4577 sp_style_read_from_repr (query, repr);
4578 }
4579 else
4580 {
4581 return;
4582 }
4583 }
4585 if (query->text)
4586 {
4587 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4588 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4589 gtk_entry_set_text (GTK_ENTRY (entry), "");
4591 } else if (query->text->font_specification.value || query->text->font_family.value) {
4593 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4595 // Get the font that corresponds
4596 Glib::ustring familyName;
4598 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4599 if (font) {
4600 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4601 font->Unref();
4602 font = NULL;
4603 }
4605 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4607 Gtk::TreePath path;
4608 try {
4609 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4610 } catch (...) {
4611 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4612 return;
4613 }
4615 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4616 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4618 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4620 gtk_tree_selection_select_path (tselection, path.gobj());
4621 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4623 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4624 }
4626 //Size
4627 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4628 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4629 g_object_set_data (tbl, "size-block", gpointer(1));
4630 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4631 g_object_set_data (tbl, "size-block", gpointer(0));
4632 free (str);
4634 //Anchor
4635 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4636 {
4637 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4638 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4639 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4640 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4641 }
4642 else
4643 {
4644 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4645 {
4646 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4647 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4648 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4649 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4650 }
4651 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4652 {
4653 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4654 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4655 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4656 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4657 }
4658 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4659 {
4660 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4661 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4662 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4663 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4664 }
4665 }
4667 //Style
4668 {
4669 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4671 gboolean active = gtk_toggle_button_get_active (button);
4672 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4674 if (active != check)
4675 {
4676 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4677 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4678 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4679 }
4680 }
4682 {
4683 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4685 gboolean active = gtk_toggle_button_get_active (button);
4686 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4688 if (active != check)
4689 {
4690 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4691 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4692 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4693 }
4694 }
4696 //Orientation
4697 //locking both buttons, changing one affect all group (both)
4698 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4699 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4701 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4702 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4704 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4705 {
4706 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4707 }
4708 else
4709 {
4710 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4711 }
4712 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4713 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4714 }
4716 sp_style_unref(query);
4717 }
4719 void
4720 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4721 {
4722 sp_text_toolbox_selection_changed (selection, tbl);
4723 }
4725 void
4726 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4727 {
4728 sp_text_toolbox_selection_changed (NULL, tbl);
4729 }
4731 void
4732 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
4733 GObject *tbl)
4734 {
4735 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4736 GtkTreeModel *model = 0;
4737 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4738 GtkTreeIter iter;
4739 char *family = 0;
4741 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4742 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4744 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4745 return;
4746 }
4748 gtk_tree_model_get (model, &iter, 0, &family, -1);
4750 if (g_object_get_data (G_OBJECT (selection), "block"))
4751 {
4752 gtk_entry_set_text (GTK_ENTRY (entry), family);
4753 return;
4754 }
4756 gtk_entry_set_text (GTK_ENTRY (entry), family);
4758 SPStyle *query =
4759 sp_style_new (SP_ACTIVE_DOCUMENT);
4761 int result_fontspec =
4762 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4764 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4766 SPCSSAttr *css = sp_repr_css_attr_new ();
4769 // First try to get the font spec from the stored value
4770 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4772 if (fontSpec.empty()) {
4773 // Construct a new font specification if it does not yet exist
4774 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4775 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4776 fontFromStyle->Unref();
4777 }
4779 if (!fontSpec.empty()) {
4780 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4781 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4782 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4783 if (font) {
4784 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4786 // Set all the these just in case they were altered when finding the best
4787 // match for the new family and old style...
4789 gchar c[256];
4791 font->Family(c, 256);
4792 sp_repr_css_set_property (css, "font-family", c);
4794 font->Attribute( "weight", c, 256);
4795 sp_repr_css_set_property (css, "font-weight", c);
4797 font->Attribute("style", c, 256);
4798 sp_repr_css_set_property (css, "font-style", c);
4800 font->Attribute("stretch", c, 256);
4801 sp_repr_css_set_property (css, "font-stretch", c);
4803 font->Attribute("variant", c, 256);
4804 sp_repr_css_set_property (css, "font-variant", c);
4806 font->Unref();
4807 }
4808 }
4809 }
4811 // If querying returned nothing, set the default style of the tool (for new texts)
4812 if (result_fontspec == QUERY_STYLE_NOTHING)
4813 {
4814 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4815 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4816 }
4817 else
4818 {
4819 sp_desktop_set_style (desktop, css, true, true);
4820 }
4822 sp_style_unref(query);
4824 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4825 _("Text: Change font family"));
4826 sp_repr_css_attr_unref (css);
4827 free (family);
4828 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4830 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4831 }
4833 void
4834 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
4835 GObject *tbl)
4836 {
4837 const char *family = gtk_entry_get_text (entry);
4839 try {
4840 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4841 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4842 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4843 gtk_tree_selection_select_path (selection, path.gobj());
4844 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4845 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4846 } catch (...) {
4847 if (family && strlen (family))
4848 {
4849 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4850 }
4851 }
4852 }
4854 void
4855 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
4856 gpointer data)
4857 {
4858 if (g_object_get_data (G_OBJECT (button), "block")) return;
4859 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4860 int prop = GPOINTER_TO_INT(data);
4862 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4863 SPCSSAttr *css = sp_repr_css_attr_new ();
4865 switch (prop)
4866 {
4867 case 0:
4868 {
4869 sp_repr_css_set_property (css, "text-anchor", "start");
4870 sp_repr_css_set_property (css, "text-align", "start");
4871 break;
4872 }
4873 case 1:
4874 {
4875 sp_repr_css_set_property (css, "text-anchor", "middle");
4876 sp_repr_css_set_property (css, "text-align", "center");
4877 break;
4878 }
4880 case 2:
4881 {
4882 sp_repr_css_set_property (css, "text-anchor", "end");
4883 sp_repr_css_set_property (css, "text-align", "end");
4884 break;
4885 }
4887 case 3:
4888 {
4889 sp_repr_css_set_property (css, "text-anchor", "start");
4890 sp_repr_css_set_property (css, "text-align", "justify");
4891 break;
4892 }
4893 }
4895 SPStyle *query =
4896 sp_style_new (SP_ACTIVE_DOCUMENT);
4897 int result_numbers =
4898 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4900 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4901 if (result_numbers == QUERY_STYLE_NOTHING)
4902 {
4903 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4904 }
4906 sp_style_unref(query);
4908 sp_desktop_set_style (desktop, css, true, true);
4909 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4910 _("Text: Change alignment"));
4911 sp_repr_css_attr_unref (css);
4913 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4914 }
4916 void
4917 sp_text_toolbox_style_toggled (GtkToggleButton *button,
4918 gpointer data)
4919 {
4920 if (g_object_get_data (G_OBJECT (button), "block")) return;
4922 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4923 SPCSSAttr *css = sp_repr_css_attr_new ();
4924 int prop = GPOINTER_TO_INT(data);
4925 bool active = gtk_toggle_button_get_active (button);
4927 SPStyle *query =
4928 sp_style_new (SP_ACTIVE_DOCUMENT);
4930 int result_fontspec =
4931 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4933 int result_family =
4934 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4936 int result_style =
4937 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4939 int result_numbers =
4940 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4942 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4943 Glib::ustring newFontSpec = "";
4945 if (fontSpec.empty()) {
4946 // Construct a new font specification if it does not yet exist
4947 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4948 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4949 fontFromStyle->Unref();
4950 }
4952 switch (prop)
4953 {
4954 case 0:
4955 {
4956 if (!fontSpec.empty()) {
4957 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4958 }
4959 if (fontSpec != newFontSpec) {
4960 // Don't even set the bold if the font didn't exist on the system
4961 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4962 }
4963 break;
4964 }
4966 case 1:
4967 {
4968 if (!fontSpec.empty()) {
4969 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4970 }
4971 if (fontSpec != newFontSpec) {
4972 // Don't even set the italic if the font didn't exist on the system
4973 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4974 }
4975 break;
4976 }
4977 }
4979 if (!newFontSpec.empty()) {
4980 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4981 }
4983 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4984 if (result_fontspec == QUERY_STYLE_NOTHING)
4985 {
4986 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4987 }
4989 sp_style_unref(query);
4991 sp_desktop_set_style (desktop, css, true, true);
4992 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4993 _("Text: Change font style"));
4994 sp_repr_css_attr_unref (css);
4996 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4997 }
4999 void
5000 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
5001 gpointer data)
5002 {
5003 if (g_object_get_data (G_OBJECT (button), "block")) {
5004 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5005 return;
5006 }
5008 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5009 SPCSSAttr *css = sp_repr_css_attr_new ();
5010 int prop = GPOINTER_TO_INT(data);
5012 switch (prop)
5013 {
5014 case 0:
5015 {
5016 sp_repr_css_set_property (css, "writing-mode", "lr");
5017 break;
5018 }
5020 case 1:
5021 {
5022 sp_repr_css_set_property (css, "writing-mode", "tb");
5023 break;
5024 }
5025 }
5027 SPStyle *query =
5028 sp_style_new (SP_ACTIVE_DOCUMENT);
5029 int result_numbers =
5030 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5032 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5033 if (result_numbers == QUERY_STYLE_NOTHING)
5034 {
5035 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5036 }
5038 sp_desktop_set_style (desktop, css, true, true);
5039 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5040 _("Text: Change orientation"));
5041 sp_repr_css_attr_unref (css);
5043 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5044 }
5046 gboolean
5047 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5048 {
5049 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5050 if (!desktop) return FALSE;
5052 switch (get_group0_keyval (event)) {
5053 case GDK_Escape: // defocus
5054 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5055 sp_text_toolbox_selection_changed (NULL, tbl); // update
5056 return TRUE; // I consumed the event
5057 break;
5058 }
5059 return FALSE;
5060 }
5062 gboolean
5063 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5064 {
5065 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5066 if (!desktop) return FALSE;
5068 switch (get_group0_keyval (event)) {
5069 case GDK_KP_Enter:
5070 case GDK_Return:
5071 case GDK_Escape: // defocus
5072 gtk_widget_hide (w);
5073 popdown_visible = false;
5074 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5075 return TRUE; // I consumed the event
5076 break;
5077 case GDK_w:
5078 case GDK_W:
5079 if (event->state & GDK_CONTROL_MASK) {
5080 gtk_widget_hide (w);
5081 popdown_visible = false;
5082 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5083 return TRUE; // I consumed the event
5084 }
5085 break;
5086 }
5087 return FALSE;
5088 }
5091 void
5092 sp_text_toolbox_size_changed (GtkComboBox *cbox,
5093 GObject *tbl)
5094 {
5095 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5097 if (g_object_get_data (tbl, "size-block")) return;
5099 // If this is not from selecting a size in the list (in which case get_active will give the
5100 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5101 // process this event. This fixes GTK's stupid insistence on sending an activate change every
5102 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5103 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5104 return;
5106 gchar *endptr;
5107 gdouble value = -1;
5108 char *text = gtk_combo_box_get_active_text (cbox);
5109 if (text) {
5110 value = g_strtod (text, &endptr);
5111 if (endptr == text) // conversion failed, non-numeric input
5112 value = -1;
5113 free (text);
5114 }
5115 if (value <= 0) {
5116 return; // could not parse value
5117 }
5119 SPCSSAttr *css = sp_repr_css_attr_new ();
5120 Inkscape::CSSOStringStream osfs;
5121 osfs << value;
5122 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5124 SPStyle *query =
5125 sp_style_new (SP_ACTIVE_DOCUMENT);
5126 int result_numbers =
5127 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5129 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5130 if (result_numbers == QUERY_STYLE_NOTHING)
5131 {
5132 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5133 }
5135 sp_style_unref(query);
5137 sp_desktop_set_style (desktop, css, true, true);
5138 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5139 _("Text: Change font size"));
5140 sp_repr_css_attr_unref (css);
5142 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5143 }
5145 gboolean
5146 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5147 {
5148 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5149 if (!desktop) return FALSE;
5151 if (!g_object_get_data (tbl, "esc-pressed")) {
5152 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5153 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5154 sp_text_toolbox_size_changed (cbox, tbl);
5155 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5156 }
5157 return FALSE; // I consumed the event
5158 }
5161 gboolean
5162 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5163 {
5164 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5165 if (!desktop) return FALSE;
5167 switch (get_group0_keyval (event)) {
5168 case GDK_Escape: // defocus
5169 g_object_set_data (tbl, "esc-pressed", gpointer(1));
5170 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5171 g_object_set_data (tbl, "esc-pressed", gpointer(0));
5172 return TRUE; // I consumed the event
5173 break;
5174 case GDK_Return: // defocus
5175 case GDK_KP_Enter:
5176 g_object_set_data (tbl, "enter-pressed", gpointer(1));
5177 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5178 sp_text_toolbox_size_changed (cbox, tbl);
5179 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5180 g_object_set_data (tbl, "enter-pressed", gpointer(0));
5181 return TRUE; // I consumed the event
5182 break;
5183 }
5184 return FALSE;
5185 }
5187 void
5188 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
5189 GObject *tbl)
5190 {
5191 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5192 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5193 int x, y;
5195 if (!popdown_visible)
5196 {
5197 gdk_window_get_origin (widget->window, &x, &y);
5198 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5199 gtk_widget_show_all (popdown);
5200 //sp_transientize (popdown);
5202 gdk_pointer_grab (widget->window, TRUE,
5203 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5204 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5205 GDK_POINTER_MOTION_MASK),
5206 NULL, NULL, GDK_CURRENT_TIME);
5208 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5210 popdown_visible = true;
5211 }
5212 else
5213 {
5214 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5215 gdk_pointer_ungrab (GDK_CURRENT_TIME);
5216 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5217 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5218 gtk_widget_hide (popdown);
5219 popdown_visible = false;
5220 }
5221 }
5223 gboolean
5224 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
5225 GdkEventFocus */*event*/,
5226 GObject */*tbl*/)
5227 {
5228 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5229 return FALSE;
5230 }
5232 gboolean
5233 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
5234 GdkEventFocus */*event*/,
5235 GObject */*tbl*/)
5236 {
5237 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5239 if (popdown_hasfocus) {
5240 gtk_widget_hide (popdown);
5241 popdown_hasfocus = false;
5242 popdown_visible = false;
5243 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5244 return TRUE;
5245 }
5246 return FALSE;
5247 }
5249 gboolean
5250 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
5251 GdkEventFocus */*event*/,
5252 GObject */*tbl*/)
5253 {
5254 popdown_hasfocus = true;
5255 return TRUE;
5256 }
5259 void
5260 cell_data_func (GtkTreeViewColumn */*column*/,
5261 GtkCellRenderer *cell,
5262 GtkTreeModel *tree_model,
5263 GtkTreeIter *iter,
5264 gpointer /*data*/)
5265 {
5266 char *family,
5267 *family_escaped,
5268 *sample_escaped;
5270 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5272 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
5274 family_escaped = g_markup_escape_text (family, -1);
5275 sample_escaped = g_markup_escape_text (sample, -1);
5277 std::stringstream markup;
5278 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
5279 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
5281 free (family);
5282 free (family_escaped);
5283 free (sample_escaped);
5284 }
5286 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
5287 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
5288 if (completion) {
5289 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
5290 g_object_unref (completion);
5291 }
5292 }
5294 GtkWidget*
5295 sp_text_toolbox_new (SPDesktop *desktop)
5296 {
5297 GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new());
5298 GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
5300 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
5301 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
5303 GtkTooltips *tt = gtk_tooltips_new();
5304 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
5306 ////////////Family
5307 //Window
5308 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
5309 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
5311 //Entry
5312 GtkWidget *entry = gtk_entry_new ();
5313 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
5314 GtkEntryCompletion *completion = gtk_entry_completion_new ();
5315 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
5316 gtk_entry_completion_set_text_column (completion, 0);
5317 gtk_entry_completion_set_minimum_key_length (completion, 1);
5318 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
5319 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
5320 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
5321 gtk_toolbar_append_widget( tbl, entry, "", "" );
5322 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
5324 //Button
5325 GtkWidget *button = gtk_button_new ();
5326 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
5327 gtk_toolbar_append_widget( tbl, button, "", "");
5329 //Popdown
5330 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
5331 GtkWidget *treeview = gtk_tree_view_new ();
5333 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
5334 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
5335 gtk_tree_view_column_pack_start (column, cell, FALSE);
5336 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
5337 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
5338 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
5340 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
5341 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
5342 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
5344 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
5346 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
5347 gtk_container_add (GTK_CONTAINER (sw), treeview);
5349 gtk_container_add (GTK_CONTAINER (window), sw);
5350 gtk_widget_set_size_request (window, 300, 450);
5352 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
5353 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
5354 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
5356 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
5358 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
5359 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
5360 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
5362 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
5363 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
5365 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
5366 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
5367 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
5368 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
5369 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
5371 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
5372 GtkWidget *box = gtk_event_box_new ();
5373 gtk_container_add (GTK_CONTAINER (box), image);
5374 gtk_toolbar_append_widget( tbl, box, "", "");
5375 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
5376 GtkTooltips *tooltips = gtk_tooltips_new ();
5377 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
5378 gtk_widget_hide (GTK_WIDGET (box));
5379 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
5381 ////////////Size
5382 const char *sizes[] = {
5383 "4", "6", "8", "9", "10", "11", "12", "13", "14",
5384 "16", "18", "20", "22", "24", "28",
5385 "32", "36", "40", "48", "56", "64", "72", "144"
5386 };
5388 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
5389 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
5390 gtk_widget_set_size_request (cbox, 80, -1);
5391 gtk_toolbar_append_widget( tbl, cbox, "", "");
5392 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
5393 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
5394 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
5395 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
5397 ////////////Text anchor
5398 GtkWidget *group = gtk_radio_button_new (NULL);
5399 GtkWidget *row = gtk_hbox_new (FALSE, 4);
5400 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
5402 // left
5403 GtkWidget *rbutton = group;
5404 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5405 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
5406 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5408 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5409 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
5410 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
5411 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
5413 // center
5414 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5415 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5416 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
5417 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5419 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5420 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
5421 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5422 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5424 // right
5425 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5426 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5427 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
5428 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5430 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5431 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5432 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5433 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5435 // fill
5436 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5437 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5438 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
5439 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5441 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5442 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
5443 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5444 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5446 gtk_toolbar_append_widget( tbl, row, "", "");
5448 //spacer
5449 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5451 ////////////Text style
5452 row = gtk_hbox_new (FALSE, 4);
5454 // bold
5455 rbutton = gtk_toggle_button_new ();
5456 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5457 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
5458 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5459 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5461 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5462 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
5463 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5465 // italic
5466 rbutton = gtk_toggle_button_new ();
5467 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5468 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
5469 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5470 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5472 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5473 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
5474 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5476 gtk_toolbar_append_widget( tbl, row, "", "");
5478 //spacer
5479 gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
5481 ////////////Text orientation
5482 group = gtk_radio_button_new (NULL);
5483 row = gtk_hbox_new (FALSE, 4);
5484 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5486 // horizontal
5487 rbutton = group;
5488 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5489 gtk_container_add (GTK_CONTAINER (rbutton),
5490 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
5491 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5492 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5494 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5495 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5496 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5498 // vertical
5499 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5500 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5501 gtk_container_add (GTK_CONTAINER (rbutton),
5502 sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
5503 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5504 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5506 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5507 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
5508 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5509 gtk_toolbar_append_widget( tbl, row, "", "" );
5512 //watch selection
5513 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5515 sigc::connection *c_selection_changed =
5516 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5517 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5518 pool->add_connection ("selection-changed", c_selection_changed);
5520 sigc::connection *c_selection_modified =
5521 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5522 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5523 pool->add_connection ("selection-modified", c_selection_modified);
5525 sigc::connection *c_subselection_changed =
5526 new sigc::connection (desktop->connectToolSubselectionChanged
5527 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5528 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5530 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5533 gtk_widget_show_all( GTK_WIDGET(tbl) );
5535 return GTK_WIDGET(tbl);
5536 } // end of sp_text_toolbox_new()
5538 }//<unnamed> namespace
5541 //#########################
5542 //## Connector ##
5543 //#########################
5545 static void sp_connector_path_set_avoid(void)
5546 {
5547 cc_selection_set_avoid(true);
5548 }
5551 static void sp_connector_path_set_ignore(void)
5552 {
5553 cc_selection_set_avoid(false);
5554 }
5558 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5559 {
5560 // quit if run by the _changed callbacks
5561 if (g_object_get_data( tbl, "freeze" )) {
5562 return;
5563 }
5565 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5566 SPDocument *doc = sp_desktop_document(desktop);
5568 if (!sp_document_get_undo_sensitive(doc))
5569 {
5570 return;
5571 }
5573 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5575 if ( repr->attribute("inkscape:connector-spacing") ) {
5576 gdouble priorValue = gtk_adjustment_get_value(adj);
5577 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5578 if ( priorValue == gtk_adjustment_get_value(adj) ) {
5579 return;
5580 }
5581 } else if ( adj->value == defaultConnSpacing ) {
5582 return;
5583 }
5585 // in turn, prevent callbacks from responding
5586 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5588 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5589 SP_OBJECT(desktop->namedview)->updateRepr();
5591 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5592 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5593 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5594 NR::Matrix m = NR::identity();
5595 avoid_item_move(&m, item);
5596 }
5598 if (items) {
5599 g_slist_free(items);
5600 }
5602 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5603 _("Change connector spacing"));
5605 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5607 spinbutton_defocus(GTK_OBJECT(tbl));
5608 }
5610 static void sp_connector_graph_layout(void)
5611 {
5612 if (!SP_ACTIVE_DESKTOP) return;
5614 // hack for clones, see comment in align-and-distribute.cpp
5615 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5616 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5618 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5620 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5622 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5623 }
5625 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5626 {
5627 if ( gtk_toggle_action_get_active( act ) ) {
5628 prefs_set_string_attribute("tools.connector", "directedlayout",
5629 "true");
5630 } else {
5631 prefs_set_string_attribute("tools.connector", "directedlayout",
5632 "false");
5633 }
5634 }
5636 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5637 {
5638 if ( gtk_toggle_action_get_active( act ) ) {
5639 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5640 "true");
5641 } else {
5642 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5643 "false");
5644 }
5645 }
5648 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5649 {
5650 prefs_set_double_attribute("tools.connector", "length", adj->value);
5651 spinbutton_defocus(GTK_OBJECT(tbl));
5652 }
5654 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5655 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5656 bool /*is_interactive*/, gpointer data)
5657 {
5658 GtkWidget *tbl = GTK_WIDGET(data);
5660 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5661 return;
5662 }
5663 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5664 return;
5665 }
5667 GtkAdjustment *adj = (GtkAdjustment*)
5668 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5669 gdouble spacing = defaultConnSpacing;
5670 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5672 gtk_adjustment_set_value(adj, spacing);
5673 }
5676 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5677 NULL, /* child_added */
5678 NULL, /* child_removed */
5679 connector_tb_event_attr_changed,
5680 NULL, /* content_changed */
5681 NULL /* order_changed */
5682 };
5685 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5686 {
5687 Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
5689 {
5690 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5691 _("Avoid"),
5692 _("Make connectors avoid selected objects"),
5693 "connector_avoid",
5694 secondarySize );
5695 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5696 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5697 }
5699 {
5700 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5701 _("Ignore"),
5702 _("Make connectors ignore selected objects"),
5703 "connector_ignore",
5704 secondarySize );
5705 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5706 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5707 }
5709 EgeAdjustmentAction* eact = 0;
5711 // Spacing spinbox
5712 eact = create_adjustment_action( "ConnectorSpacingAction",
5713 _("Connector Spacing"), _("Spacing:"),
5714 _("The amount of space left around objects by auto-routing connectors"),
5715 "tools.connector", "spacing", defaultConnSpacing,
5716 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5717 0, 100, 1.0, 10.0,
5718 0, 0, 0,
5719 connector_spacing_changed, 1, 0 );
5720 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5722 // Graph (connector network) layout
5723 {
5724 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5725 _("Graph"),
5726 _("Nicely arrange selected connector network"),
5727 "graph_layout",
5728 secondarySize );
5729 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5730 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5731 }
5733 // Default connector length spinbox
5734 eact = create_adjustment_action( "ConnectorLengthAction",
5735 _("Connector Length"), _("Length:"),
5736 _("Ideal length for connectors when layout is applied"),
5737 "tools.connector", "length", 100,
5738 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5739 10, 1000, 10.0, 100.0,
5740 0, 0, 0,
5741 connector_length_changed, 1, 0 );
5742 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5745 // Directed edges toggle button
5746 {
5747 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5748 _("Downwards"),
5749 _("Make connectors with end-markers (arrows) point downwards"),
5750 "directed_graph",
5751 Inkscape::ICON_SIZE_DECORATION );
5752 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5754 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5755 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5756 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5758 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5759 }
5761 // Avoid overlaps toggle button
5762 {
5763 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5764 _("Remove overlaps"),
5765 _("Do not allow overlapping shapes"),
5766 "remove_overlaps",
5767 Inkscape::ICON_SIZE_DECORATION );
5768 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5770 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5771 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5772 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5774 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5775 }
5777 // Code to watch for changes to the connector-spacing attribute in
5778 // the XML.
5779 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5780 g_assert(repr != NULL);
5782 purge_repr_listener( holder, holder );
5784 if (repr) {
5785 g_object_set_data( holder, "repr", repr );
5786 Inkscape::GC::anchor(repr);
5787 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5788 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5789 }
5790 } // end of sp_connector_toolbox_prep()
5793 //#########################
5794 //## Paintbucket ##
5795 //#########################
5797 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5798 {
5799 gint channels = ege_select_one_action_get_active( act );
5800 flood_channels_set_channels( channels );
5801 }
5803 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5804 {
5805 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5806 }
5808 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5809 {
5810 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5811 }
5813 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5814 {
5815 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5816 SPUnit const *unit = tracker->getActiveUnit();
5818 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5820 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5821 }
5823 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5824 {
5825 // FIXME: make defaults settable via Inkscape Options
5826 struct KeyValue {
5827 char const *key;
5828 double value;
5829 } const key_values[] = {
5830 {"threshold", 15},
5831 {"offset", 0.0}
5832 };
5834 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5835 KeyValue const &kv = key_values[i];
5836 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5837 if ( adj ) {
5838 gtk_adjustment_set_value(adj, kv.value);
5839 }
5840 }
5842 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5843 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5844 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5845 ege_select_one_action_set_active( autogap_action, 0 );
5846 }
5848 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5849 {
5850 EgeAdjustmentAction* eact = 0;
5852 {
5853 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5855 GList* items = 0;
5856 gint count = 0;
5857 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5858 {
5859 GtkTreeIter iter;
5860 gtk_list_store_append( model, &iter );
5861 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5862 count++;
5863 }
5864 g_list_free( items );
5865 items = 0;
5866 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5867 g_object_set( act1, "short_label", _("Fill by:"), NULL );
5868 ege_select_one_action_set_appearance( act1, "compact" );
5869 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5870 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5871 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5872 g_object_set_data( holder, "channels_action", act1 );
5873 }
5875 // Spacing spinbox
5876 {
5877 eact = create_adjustment_action(
5878 "ThresholdAction",
5879 _("Fill Threshold"), _("Threshold:"),
5880 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5881 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5882 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5883 0, 0, 0,
5884 paintbucket_threshold_changed, 1, 0 );
5886 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5887 }
5889 // Create the units menu.
5890 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5891 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5892 if (stored_unit)
5893 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5894 g_object_set_data( holder, "tracker", tracker );
5895 {
5896 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5897 gtk_action_group_add_action( mainActions, act );
5898 }
5900 // Offset spinbox
5901 {
5902 eact = create_adjustment_action(
5903 "OffsetAction",
5904 _("Grow/shrink by"), _("Grow/shrink by:"),
5905 _("The amount to grow (positive) or shrink (negative) the created fill path"),
5906 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5907 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5908 0, 0, 0,
5909 paintbucket_offset_changed, 1, 2);
5910 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5912 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5913 }
5915 /* Auto Gap */
5916 {
5917 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5919 GList* items = 0;
5920 gint count = 0;
5921 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5922 {
5923 GtkTreeIter iter;
5924 gtk_list_store_append( model, &iter );
5925 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5926 count++;
5927 }
5928 g_list_free( items );
5929 items = 0;
5930 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5931 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5932 ege_select_one_action_set_appearance( act2, "compact" );
5933 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5934 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5935 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5936 g_object_set_data( holder, "autogap_action", act2 );
5937 }
5939 /* Reset */
5940 {
5941 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5942 _("Defaults"),
5943 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5944 GTK_STOCK_CLEAR );
5945 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5946 gtk_action_group_add_action( mainActions, act );
5947 gtk_action_set_sensitive( act, TRUE );
5948 }
5950 }
5952 /*
5953 Local Variables:
5954 mode:c++
5955 c-file-style:"stroustrup"
5956 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5957 indent-tabs-mode:nil
5958 fill-column:99
5959 End:
5960 */
5961 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :