8ac019cb7921b5b829de8ec2d1ec215ec7f10828
1 /** \file
2 * Controls bars for some of Inkscape's tools
3 * (for some tools, they are in their own files)
4 */
6 /*
7 *
8 * Authors:
9 * MenTaLguY <mental@rydia.net>
10 * Lauris Kaplinski <lauris@kaplinski.com>
11 * bulia byak <buliabyak@users.sf.net>
12 * Frank Felfe <innerspace@iname.com>
13 * John Cliff <simarilius@yahoo.com>
14 * David Turner <novalis@gnu.org>
15 * Josh Andler <scislac@scislac.com>
16 * Jon A. Cruz <jon@joncruz.org>
17 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2006 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <cstring>
31 #include <string>
33 #include <gtkmm.h>
34 #include <gtk/gtk.h>
35 #include <iostream>
36 #include <sstream>
38 #include "widgets/button.h"
39 #include "widgets/widget-sizes.h"
40 #include "widgets/spw-utilities.h"
41 #include "widgets/spinbutton-events.h"
42 #include "dialogs/text-edit.h"
43 #include "dialogs/dialog-events.h"
45 #include "ui/widget/style-swatch.h"
47 #include "prefs-utils.h"
48 #include "verbs.h"
49 #include "sp-namedview.h"
50 #include "desktop.h"
51 #include "desktop-handles.h"
52 #include "xml/repr.h"
53 #include "xml/node-event-vector.h"
54 #include <glibmm/i18n.h>
55 #include "helper/unit-menu.h"
56 #include "helper/units.h"
58 #include "inkscape.h"
59 #include "conn-avoid-ref.h"
62 #include "select-toolbar.h"
63 #include "gradient-toolbar.h"
65 #include "connector-context.h"
66 #include "node-context.h"
67 #include "shape-editor.h"
68 #include "tweak-context.h"
69 #include "sp-rect.h"
70 #include "box3d.h"
71 #include "box3d-context.h"
72 #include "sp-star.h"
73 #include "sp-spiral.h"
74 #include "sp-ellipse.h"
75 #include "sp-text.h"
76 #include "sp-flowtext.h"
77 #include "sp-clippath.h"
78 #include "sp-mask.h"
79 #include "style.h"
80 #include "selection.h"
81 #include "selection-chemistry.h"
82 #include "document-private.h"
83 #include "desktop-style.h"
84 #include "../libnrtype/font-lister.h"
85 #include "../libnrtype/font-instance.h"
86 #include "../connection-pool.h"
87 #include "../prefs-utils.h"
88 #include "../inkscape-stock.h"
89 #include "icon.h"
90 #include "graphlayout/graphlayout.h"
92 #include "mod360.h"
94 #include "toolbox.h"
96 #include "flood-context.h"
98 #include "ink-action.h"
99 #include "ege-adjustment-action.h"
100 #include "ege-output-action.h"
101 #include "ege-select-one-action.h"
102 #include "helper/unit-tracker.h"
104 #include "svg/css-ostringstream.h"
106 using Inkscape::UnitTracker;
108 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
109 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
111 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
124 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
130 static struct {
131 gchar const *type_name;
132 gchar const *data_name;
133 sp_verb_t verb;
134 sp_verb_t doubleclick_verb;
135 } const tools[] = {
136 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
137 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
138 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
139 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
140 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
141 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
142 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
143 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
144 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
145 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
146 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
147 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
148 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
149 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
150 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
151 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
152 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
153 { NULL, NULL, 0, 0 }
154 };
156 static struct {
157 gchar const *type_name;
158 gchar const *data_name;
159 GtkWidget *(*create_func)(SPDesktop *desktop);
160 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
161 gchar const *ui_name;
162 gint swatch_verb_id;
163 gchar const *swatch_tool;
164 gchar const *swatch_tip;
165 } const aux_toolboxes[] = {
166 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
167 SP_VERB_INVALID, 0, 0},
168 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
169 SP_VERB_INVALID, 0, 0},
170 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
171 SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
172 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
173 SP_VERB_INVALID, 0, 0},
174 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
175 SP_VERB_CONTEXT_STAR_PREFS, "tools.shapes.star", _("Style of new stars")},
176 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
177 SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", _("Style of new rectangles")},
178 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
179 SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", _("Style of new 3D boxes")},
180 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
181 SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", _("Style of new ellipses")},
182 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
183 SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral", _("Style of new spirals")},
184 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
185 SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
186 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
187 SP_VERB_CONTEXT_PEN_PREFS, "tools.freehand.pen", _("Style of new paths created by Pen")},
188 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
189 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
190 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
191 SP_VERB_INVALID, 0, 0},
192 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
193 SP_VERB_INVALID, 0, 0},
194 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
195 SP_VERB_INVALID, 0, 0},
196 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
197 SP_VERB_INVALID, 0, 0},
198 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
199 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
200 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
201 };
204 static gchar const * ui_descr =
205 "<ui>"
206 " <toolbar name='SelectToolbar'>"
207 " <toolitem action='EditSelectAll' />"
208 " <toolitem action='EditSelectAllInAllLayers' />"
209 " <toolitem action='EditDeselect' />"
210 " <separator />"
211 " <toolitem action='ObjectRotate90CCW' />"
212 " <toolitem action='ObjectRotate90' />"
213 " <toolitem action='ObjectFlipHorizontally' />"
214 " <toolitem action='ObjectFlipVertically' />"
215 " <separator />"
216 " <toolitem action='SelectionToBack' />"
217 " <toolitem action='SelectionLower' />"
218 " <toolitem action='SelectionRaise' />"
219 " <toolitem action='SelectionToFront' />"
220 " <separator />"
221 " <toolitem action='XAction' />"
222 " <toolitem action='YAction' />"
223 " <toolitem action='WidthAction' />"
224 " <toolitem action='LockAction' />"
225 " <toolitem action='HeightAction' />"
226 " <toolitem action='UnitsAction' />"
227 " <separator />"
228 " <toolitem action='transform_affect_label' />"
229 " <toolitem action='transform_stroke' />"
230 " <toolitem action='transform_corners' />"
231 " <toolitem action='transform_gradient' />"
232 " <toolitem action='transform_pattern' />"
233 " </toolbar>"
235 " <toolbar name='NodeToolbar'>"
236 " <toolitem action='NodeInsertAction' />"
237 " <toolitem action='NodeDeleteAction' />"
238 " <separator />"
239 " <toolitem action='NodeJoinAction' />"
240 " <toolitem action='NodeJoinSegmentAction' />"
241 " <toolitem action='NodeDeleteSegmentAction' />"
242 " <toolitem action='NodeBreakAction' />"
243 " <separator />"
244 " <toolitem action='NodeCuspAction' />"
245 " <toolitem action='NodeSmoothAction' />"
246 " <toolitem action='NodeSymmetricAction' />"
247 " <separator />"
248 " <toolitem action='NodeLineAction' />"
249 " <toolitem action='NodeCurveAction' />"
250 " <separator />"
251 " <toolitem action='ObjectToPath' />"
252 " <toolitem action='StrokeToPath' />"
253 " <separator />"
254 " <toolitem action='NodeXAction' />"
255 " <toolitem action='NodeYAction' />"
256 " <toolitem action='NodeUnitsAction' />"
257 " <separator />"
258 " <toolitem action='ObjectEditClipPathAction' />"
259 " <toolitem action='ObjectEditMaskPathAction' />"
260 " <toolitem action='EditNextLPEParameterAction' />"
261 " <separator />"
262 " <toolitem action='NodesShowHandlesAction' />"
263 " <toolitem action='NodesShowHelperpath' />"
264 " </toolbar>"
266 " <toolbar name='TweakToolbar'>"
267 " <toolitem action='TweakWidthAction' />"
268 " <separator />"
269 " <toolitem action='TweakForceAction' />"
270 " <toolitem action='TweakPressureAction' />"
271 " <separator />"
272 " <toolitem action='TweakModeAction' />"
273 " <separator />"
274 " <toolitem action='TweakFidelityAction' />"
275 " <separator />"
276 " <toolitem action='TweakChannelsLabel' />"
277 " <toolitem action='TweakDoH' />"
278 " <toolitem action='TweakDoS' />"
279 " <toolitem action='TweakDoL' />"
280 " <toolitem action='TweakDoO' />"
281 " </toolbar>"
283 " <toolbar name='ZoomToolbar'>"
284 " <toolitem action='ZoomIn' />"
285 " <toolitem action='ZoomOut' />"
286 " <separator />"
287 " <toolitem action='Zoom1:0' />"
288 " <toolitem action='Zoom1:2' />"
289 " <toolitem action='Zoom2:1' />"
290 " <separator />"
291 " <toolitem action='ZoomSelection' />"
292 " <toolitem action='ZoomDrawing' />"
293 " <toolitem action='ZoomPage' />"
294 " <toolitem action='ZoomPageWidth' />"
295 " <separator />"
296 " <toolitem action='ZoomPrev' />"
297 " <toolitem action='ZoomNext' />"
298 " </toolbar>"
300 " <toolbar name='StarToolbar'>"
301 " <separator />"
302 " <toolitem action='StarStateAction' />"
303 " <separator />"
304 " <toolitem action='FlatAction' />"
305 " <separator />"
306 " <toolitem action='MagnitudeAction' />"
307 " <toolitem action='SpokeAction' />"
308 " <toolitem action='RoundednessAction' />"
309 " <toolitem action='RandomizationAction' />"
310 " <separator />"
311 " <toolitem action='StarResetAction' />"
312 " </toolbar>"
314 " <toolbar name='RectToolbar'>"
315 " <toolitem action='RectStateAction' />"
316 " <toolitem action='RectWidthAction' />"
317 " <toolitem action='RectHeightAction' />"
318 " <toolitem action='RadiusXAction' />"
319 " <toolitem action='RadiusYAction' />"
320 " <toolitem action='RectUnitsAction' />"
321 " <separator />"
322 " <toolitem action='RectResetAction' />"
323 " </toolbar>"
325 " <toolbar name='3DBoxToolbar'>"
326 " <toolitem action='3DBoxAngleXAction' />"
327 " <toolitem action='3DBoxVPXStateAction' />"
328 " <separator />"
329 " <toolitem action='3DBoxAngleYAction' />"
330 " <toolitem action='3DBoxVPYStateAction' />"
331 " <separator />"
332 " <toolitem action='3DBoxAngleZAction' />"
333 " <toolitem action='3DBoxVPZStateAction' />"
334 " </toolbar>"
336 " <toolbar name='SpiralToolbar'>"
337 " <toolitem action='SpiralStateAction' />"
338 " <toolitem action='SpiralRevolutionAction' />"
339 " <toolitem action='SpiralExpansionAction' />"
340 " <toolitem action='SpiralT0Action' />"
341 " <separator />"
342 " <toolitem action='SpiralResetAction' />"
343 " </toolbar>"
345 " <toolbar name='PenToolbar'>"
346 " </toolbar>"
348 " <toolbar name='PencilToolbar'>"
349 " </toolbar>"
351 " <toolbar name='CalligraphyToolbar'>"
352 " <separator />"
353 " <toolitem action='SetProfileAction'/>"
354 " <separator />"
355 " <toolitem action='CalligraphyWidthAction' />"
356 " <toolitem action='PressureAction' />"
357 " <toolitem action='TraceAction' />"
358 " <toolitem action='ThinningAction' />"
359 " <separator />"
360 " <toolitem action='AngleAction' />"
361 " <toolitem action='TiltAction' />"
362 " <toolitem action='FixationAction' />"
363 " <separator />"
364 " <toolitem action='CapRoundingAction' />"
365 " <separator />"
366 " <toolitem action='TremorAction' />"
367 " <toolitem action='WiggleAction' />"
368 " <toolitem action='MassAction' />"
369 " <separator />"
370 " </toolbar>"
372 " <toolbar name='ArcToolbar'>"
373 " <toolitem action='ArcStateAction' />"
374 " <separator />"
375 " <toolitem action='ArcStartAction' />"
376 " <toolitem action='ArcEndAction' />"
377 " <separator />"
378 " <toolitem action='ArcOpenAction' />"
379 " <separator />"
380 " <toolitem action='ArcResetAction' />"
381 " <separator />"
382 " </toolbar>"
384 " <toolbar name='PaintbucketToolbar'>"
385 " <toolitem action='ChannelsAction' />"
386 " <separator />"
387 " <toolitem action='ThresholdAction' />"
388 " <separator />"
389 " <toolitem action='OffsetAction' />"
390 " <toolitem action='PaintbucketUnitsAction' />"
391 " <separator />"
392 " <toolitem action='AutoGapAction' />"
393 " <separator />"
394 " <toolitem action='PaintbucketResetAction' />"
395 " </toolbar>"
397 " <toolbar name='DropperToolbar'>"
398 " <toolitem action='DropperOpacityAction' />"
399 " <toolitem action='DropperPickAlphaAction' />"
400 " <toolitem action='DropperSetAlphaAction' />"
401 " </toolbar>"
403 " <toolbar name='ConnectorToolbar'>"
404 " <toolitem action='ConnectorAvoidAction' />"
405 " <toolitem action='ConnectorIgnoreAction' />"
406 " <toolitem action='ConnectorSpacingAction' />"
407 " <toolitem action='ConnectorGraphAction' />"
408 " <toolitem action='ConnectorLengthAction' />"
409 " <toolitem action='ConnectorDirectedAction' />"
410 " <toolitem action='ConnectorOverlapAction' />"
411 " </toolbar>"
413 "</ui>"
414 ;
416 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
418 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
420 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
421 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
423 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
424 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
426 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
427 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
429 /* Global text entry widgets necessary for update */
430 /* GtkWidget *dropper_rgb_entry,
431 *dropper_opacity_entry ; */
432 // should be made a private member once this is converted to class
434 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
435 connection->disconnect();
436 delete connection;
437 }
439 static void purge_repr_listener( GObject* obj, GObject* tbl )
440 {
441 (void)obj;
442 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
443 if (oldrepr) { // remove old listener
444 sp_repr_remove_listener_by_data(oldrepr, tbl);
445 Inkscape::GC::release(oldrepr);
446 oldrepr = 0;
447 g_object_set_data( tbl, "repr", NULL );
448 }
449 }
451 GtkWidget *
452 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
453 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
454 Inkscape::UI::View::View *view, GtkTooltips *tt)
455 {
456 SPAction *action = verb->get_action(view);
457 if (!action) return NULL;
459 SPAction *doubleclick_action;
460 if (doubleclick_verb)
461 doubleclick_action = doubleclick_verb->get_action(view);
462 else
463 doubleclick_action = NULL;
465 /* fixme: Handle sensitive/unsensitive */
466 /* fixme: Implement sp_button_new_from_action */
467 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
468 gtk_widget_show(b);
469 gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
471 return b;
472 }
474 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
475 Inkscape::UI::View::View *view, GtkTooltips *tt)
476 {
477 return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
478 }
480 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
481 Inkscape::UI::View::View *view, GtkTooltips *tt)
482 {
483 return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
484 }
487 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
488 {
489 SPAction* targetAction = SP_ACTION(user_data);
490 if ( targetAction ) {
491 sp_action_perform( targetAction, NULL );
492 }
493 }
495 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
496 {
497 if ( data ) {
498 GtkAction* act = GTK_ACTION(data);
499 gtk_action_set_sensitive( act, sensitive );
500 }
501 }
503 static SPActionEventVector action_event_vector = {
504 {NULL},
505 NULL,
506 NULL,
507 sp_action_action_set_sensitive,
508 NULL,
509 NULL
510 };
512 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
513 {
514 GtkAction* act = 0;
516 SPAction* targetAction = verb->get_action(view);
517 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
518 act = GTK_ACTION(inky);
519 gtk_action_set_sensitive( act, targetAction->sensitive );
521 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
523 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
524 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
526 return act;
527 }
529 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
530 {
531 Inkscape::UI::View::View *view = desktop;
532 gint verbsToUse[] = {
533 // disabled until we have icons for them:
534 //find
535 //SP_VERB_EDIT_TILE,
536 //SP_VERB_EDIT_UNTILE,
537 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
538 SP_VERB_DIALOG_DISPLAY,
539 SP_VERB_DIALOG_FILL_STROKE,
540 SP_VERB_DIALOG_NAMEDVIEW,
541 SP_VERB_DIALOG_TEXT,
542 SP_VERB_DIALOG_XML_EDITOR,
543 SP_VERB_EDIT_CLONE,
544 SP_VERB_EDIT_COPY,
545 SP_VERB_EDIT_CUT,
546 SP_VERB_EDIT_DUPLICATE,
547 SP_VERB_EDIT_PASTE,
548 SP_VERB_EDIT_REDO,
549 SP_VERB_EDIT_UNDO,
550 SP_VERB_EDIT_UNLINK_CLONE,
551 SP_VERB_FILE_EXPORT,
552 SP_VERB_FILE_IMPORT,
553 SP_VERB_FILE_NEW,
554 SP_VERB_FILE_OPEN,
555 SP_VERB_FILE_PRINT,
556 SP_VERB_FILE_SAVE,
557 SP_VERB_OBJECT_TO_CURVE,
558 SP_VERB_SELECTION_GROUP,
559 SP_VERB_SELECTION_OUTLINE,
560 SP_VERB_SELECTION_UNGROUP,
561 SP_VERB_ZOOM_1_1,
562 SP_VERB_ZOOM_1_2,
563 SP_VERB_ZOOM_2_1,
564 SP_VERB_ZOOM_DRAWING,
565 SP_VERB_ZOOM_IN,
566 SP_VERB_ZOOM_NEXT,
567 SP_VERB_ZOOM_OUT,
568 SP_VERB_ZOOM_PAGE,
569 SP_VERB_ZOOM_PAGE_WIDTH,
570 SP_VERB_ZOOM_PREV,
571 SP_VERB_ZOOM_SELECTION,
572 };
574 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
575 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
577 static std::map<SPDesktop*, GtkActionGroup*> groups;
578 GtkActionGroup* mainActions = 0;
579 if ( groups.find(desktop) != groups.end() ) {
580 mainActions = groups[desktop];
581 }
583 if ( !mainActions ) {
584 mainActions = gtk_action_group_new("main");
585 groups[desktop] = mainActions;
586 }
588 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
589 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
590 if ( verb ) {
591 if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
592 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
593 gtk_action_group_add_action( mainActions, act );
594 }
595 }
596 }
598 return mainActions;
599 }
602 GtkWidget *
603 sp_tool_toolbox_new()
604 {
605 GtkTooltips *tt = gtk_tooltips_new();
606 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
608 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
609 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
611 gtk_widget_set_sensitive(tb, FALSE);
613 GtkWidget *hb = gtk_handle_box_new();
614 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
615 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
616 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
618 gtk_container_add(GTK_CONTAINER(hb), tb);
619 gtk_widget_show(GTK_WIDGET(tb));
621 sigc::connection* conn = new sigc::connection;
622 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
624 return hb;
625 }
627 static void
628 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
629 {
630 g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
631 gtk_widget_queue_resize(child);
632 }
634 static void
635 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
636 {
637 g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
638 gtk_widget_queue_resize(child);
639 }
641 GtkWidget *
642 sp_aux_toolbox_new()
643 {
644 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
646 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
648 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
650 gtk_widget_set_sensitive(tb, FALSE);
652 GtkWidget *hb = gtk_handle_box_new();
653 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
654 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
655 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
657 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
658 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
660 gtk_container_add(GTK_CONTAINER(hb), tb);
661 gtk_widget_show(GTK_WIDGET(tb));
663 sigc::connection* conn = new sigc::connection;
664 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
666 return hb;
667 }
669 //####################################
670 //# Commands Bar
671 //####################################
673 GtkWidget *
674 sp_commands_toolbox_new()
675 {
676 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
678 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
680 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
681 gtk_widget_set_sensitive(tb, FALSE);
683 GtkWidget *hb = gtk_handle_box_new();
684 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
685 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
686 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
688 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
689 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
691 gtk_container_add(GTK_CONTAINER(hb), tb);
692 gtk_widget_show(GTK_WIDGET(tb));
694 sigc::connection* conn = new sigc::connection;
695 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
697 return hb;
698 }
700 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
701 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
702 gchar const *path, gchar const *data, gdouble def,
703 GtkWidget *focusTarget,
704 GtkWidget *us,
705 GObject *dataKludge,
706 gboolean altx, gchar const *altx_mark,
707 gdouble lower, gdouble upper, gdouble step, gdouble page,
708 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
709 void (*callback)(GtkAdjustment *, GObject *),
710 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
711 {
712 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
713 lower, upper, step, page, page ) );
714 if (us) {
715 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
716 }
718 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
720 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
721 if ( shortLabel ) {
722 g_object_set( act, "short_label", shortLabel, NULL );
723 }
725 if ( (descrCount > 0) && descrLabels && descrValues ) {
726 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
727 }
729 if ( focusTarget ) {
730 ege_adjustment_action_set_focuswidget( act, focusTarget );
731 }
733 if ( altx && altx_mark ) {
734 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
735 }
737 if ( dataKludge ) {
738 g_object_set_data( dataKludge, data, adj );
739 }
741 // Using a cast just to make sure we pass in the right kind of function pointer
742 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
744 return act;
745 }
748 //####################################
749 //# node editing callbacks
750 //####################################
752 /**
753 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
754 */
755 static ShapeEditor *get_current_shape_editor()
756 {
757 if (!SP_ACTIVE_DESKTOP) {
758 return NULL;
759 }
761 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
763 if (!SP_IS_NODE_CONTEXT(event_context)) {
764 return NULL;
765 }
767 return SP_NODE_CONTEXT(event_context)->shape_editor;
768 }
771 void
772 sp_node_path_edit_add(void)
773 {
774 ShapeEditor *shape_editor = get_current_shape_editor();
775 if (shape_editor) shape_editor->add_node();
776 }
778 void
779 sp_node_path_edit_delete(void)
780 {
781 ShapeEditor *shape_editor = get_current_shape_editor();
782 if (shape_editor) shape_editor->delete_nodes();
783 }
785 void
786 sp_node_path_edit_delete_segment(void)
787 {
788 ShapeEditor *shape_editor = get_current_shape_editor();
789 if (shape_editor) shape_editor->delete_segment();
790 }
792 void
793 sp_node_path_edit_break(void)
794 {
795 ShapeEditor *shape_editor = get_current_shape_editor();
796 if (shape_editor) shape_editor->break_at_nodes();
797 }
799 void
800 sp_node_path_edit_join(void)
801 {
802 ShapeEditor *shape_editor = get_current_shape_editor();
803 if (shape_editor) shape_editor->join_nodes();
804 }
806 void
807 sp_node_path_edit_join_segment(void)
808 {
809 ShapeEditor *shape_editor = get_current_shape_editor();
810 if (shape_editor) shape_editor->join_segments();
811 }
813 void
814 sp_node_path_edit_toline(void)
815 {
816 ShapeEditor *shape_editor = get_current_shape_editor();
817 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
818 }
820 void
821 sp_node_path_edit_tocurve(void)
822 {
823 ShapeEditor *shape_editor = get_current_shape_editor();
824 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
825 }
827 void
828 sp_node_path_edit_cusp(void)
829 {
830 ShapeEditor *shape_editor = get_current_shape_editor();
831 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
832 }
834 void
835 sp_node_path_edit_smooth(void)
836 {
837 ShapeEditor *shape_editor = get_current_shape_editor();
838 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
839 }
841 void
842 sp_node_path_edit_symmetrical(void)
843 {
844 ShapeEditor *shape_editor = get_current_shape_editor();
845 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
846 }
848 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
849 bool show = gtk_toggle_action_get_active( act );
850 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
851 ShapeEditor *shape_editor = get_current_shape_editor();
852 if (shape_editor) shape_editor->show_handles(show);
853 }
855 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
856 bool show = gtk_toggle_action_get_active( act );
857 prefs_set_int_attribute ("tools.nodes", "show_helperpath", show ? 1 : 0);
858 ShapeEditor *shape_editor = get_current_shape_editor();
859 if (shape_editor) shape_editor->show_helperpath(show);
860 }
862 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
863 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
864 }
866 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
867 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
868 }
870 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
871 sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
872 }
874 /* is called when the node selection is modified */
875 static void
876 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
877 {
878 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
879 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
880 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
881 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
883 // quit if run by the attr_changed listener
884 if (g_object_get_data( tbl, "freeze" )) {
885 return;
886 }
888 // in turn, prevent listener from responding
889 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
891 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
892 SPUnit const *unit = tracker->getActiveUnit();
894 ShapeEditor *shape_editor = get_current_shape_editor();
895 if (shape_editor && shape_editor->has_nodepath()) {
896 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
897 int n_selected = 0;
898 if (nodepath) {
899 n_selected = nodepath->numSelected();
900 }
902 if (n_selected == 0) {
903 gtk_action_set_sensitive(xact, FALSE);
904 gtk_action_set_sensitive(yact, FALSE);
905 } else {
906 gtk_action_set_sensitive(xact, TRUE);
907 gtk_action_set_sensitive(yact, TRUE);
908 NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
909 NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
911 if (n_selected == 1) {
912 NR::Point sel_node = nodepath->singleSelectedCoords();
913 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
914 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
915 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
916 }
917 } else {
918 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
919 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
920 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
921 /* Note: Currently x and y will always have a value, even if the coordinates of the
922 selected nodes don't coincide (in this case we use the coordinates of the center
923 of the bounding box). So the entries are never set to zero. */
924 // FIXME: Maybe we should clear the entry if several nodes are selected
925 // instead of providing a kind of average value
926 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
927 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
928 }
929 }
930 }
931 } else {
932 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
933 gtk_action_set_sensitive(xact, FALSE);
934 gtk_action_set_sensitive(yact, FALSE);
935 }
937 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
938 }
940 static void
941 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
942 {
943 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
945 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
946 SPUnit const *unit = tracker->getActiveUnit();
948 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
949 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
950 }
952 // quit if run by the attr_changed listener
953 if (g_object_get_data( tbl, "freeze" )) {
954 return;
955 }
957 // in turn, prevent listener from responding
958 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
960 ShapeEditor *shape_editor = get_current_shape_editor();
961 if (shape_editor && shape_editor->has_nodepath()) {
962 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
963 if (!strcmp(value_name, "x")) {
964 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
965 }
966 if (!strcmp(value_name, "y")) {
967 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
968 }
969 }
971 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
972 }
974 static void
975 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
976 {
977 sp_node_path_value_changed(adj, tbl, "x");
978 }
980 static void
981 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
982 {
983 sp_node_path_value_changed(adj, tbl, "y");
984 }
986 void
987 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
988 {
989 {
990 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
991 SPItem *item = selection->singleItem();
992 if (item && SP_IS_SHAPE(item)) {
993 LivePathEffectObject *lpeobj = sp_shape_get_livepatheffectobject(SP_SHAPE(item));
994 if (lpeobj) {
995 gtk_action_set_sensitive(w, TRUE);
996 } else {
997 gtk_action_set_sensitive(w, FALSE);
998 }
999 } else {
1000 gtk_action_set_sensitive(w, FALSE);
1001 }
1002 }
1004 {
1005 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1006 SPItem *item = selection->singleItem();
1007 if (item && item->clip_ref && item->clip_ref->getObject()) {
1008 gtk_action_set_sensitive(w, TRUE);
1009 } else {
1010 gtk_action_set_sensitive(w, FALSE);
1011 }
1012 }
1014 {
1015 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1016 SPItem *item = selection->singleItem();
1017 if (item && item->mask_ref && item->mask_ref->getObject()) {
1018 gtk_action_set_sensitive(w, TRUE);
1019 } else {
1020 gtk_action_set_sensitive(w, FALSE);
1021 }
1022 }
1023 }
1025 void
1026 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1027 {
1028 sp_node_toolbox_sel_changed (selection, tbl);
1029 }
1033 //################################
1034 //## Node Editing Toolbox ##
1035 //################################
1037 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1038 {
1039 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1040 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1041 g_object_set_data( holder, "tracker", tracker );
1043 {
1044 InkAction* inky = ink_action_new( "NodeInsertAction",
1045 _("Insert node"),
1046 _("Insert new nodes into selected segments"),
1047 "node_insert",
1048 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1049 g_object_set( inky, "short_label", _("Insert"), NULL );
1050 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1051 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1052 }
1054 {
1055 InkAction* inky = ink_action_new( "NodeDeleteAction",
1056 _("Delete node"),
1057 _("Delete selected nodes"),
1058 "node_delete",
1059 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1060 g_object_set( inky, "short_label", _("Delete"), NULL );
1061 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1062 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1063 }
1065 {
1066 InkAction* inky = ink_action_new( "NodeJoinAction",
1067 _("Join endnodes"),
1068 _("Join selected endnodes"),
1069 "node_join",
1070 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1071 g_object_set( inky, "short_label", _("Join"), NULL );
1072 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1073 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1074 }
1076 {
1077 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1078 _("Join Segment"),
1079 _("Join selected endnodes with a new segment"),
1080 "node_join_segment",
1081 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1082 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1083 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1084 }
1086 {
1087 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1088 _("Delete Segment"),
1089 _("Split path between two non-endpoint nodes"),
1090 "node_delete_segment",
1091 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1092 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1093 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1094 }
1096 {
1097 InkAction* inky = ink_action_new( "NodeBreakAction",
1098 _("Node Break"),
1099 _("Break path at selected nodes"),
1100 "node_break",
1101 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1102 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1103 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1104 }
1106 {
1107 InkAction* inky = ink_action_new( "NodeCuspAction",
1108 _("Node Cusp"),
1109 _("Make selected nodes corner"),
1110 "node_cusp",
1111 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1112 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1113 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1114 }
1116 {
1117 InkAction* inky = ink_action_new( "NodeSmoothAction",
1118 _("Node Smooth"),
1119 _("Make selected nodes smooth"),
1120 "node_smooth",
1121 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1122 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1123 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1124 }
1126 {
1127 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1128 _("Node Symmetric"),
1129 _("Make selected nodes symmetric"),
1130 "node_symmetric",
1131 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1132 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1133 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1134 }
1136 {
1137 InkAction* inky = ink_action_new( "NodeLineAction",
1138 _("Node Line"),
1139 _("Make selected segments lines"),
1140 "node_line",
1141 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1142 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1143 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1144 }
1146 {
1147 InkAction* inky = ink_action_new( "NodeCurveAction",
1148 _("Node Curve"),
1149 _("Make selected segments curves"),
1150 "node_curve",
1151 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1152 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1153 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1154 }
1156 {
1157 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1158 _("Show Handles"),
1159 _("Show the Bezier handles of selected nodes"),
1160 "nodes_show_handles",
1161 Inkscape::ICON_SIZE_DECORATION );
1162 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1163 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1164 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1165 }
1167 {
1168 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1169 _("Show Outline"),
1170 _("Show the outline of the path"),
1171 "nodes_show_helperpath",
1172 Inkscape::ICON_SIZE_DECORATION );
1173 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1174 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1175 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1176 }
1178 {
1179 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1180 _("Next path effect parameter"),
1181 _("Show next path effect parameter for editing"),
1182 "edit_next_parameter",
1183 Inkscape::ICON_SIZE_DECORATION );
1184 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1185 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1186 g_object_set_data( holder, "nodes_lpeedit", inky);
1187 }
1189 {
1190 InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1191 _("Edit clipping path"),
1192 _("Edit the clipping path of the object"),
1193 "nodeedit-clippath",
1194 Inkscape::ICON_SIZE_DECORATION );
1195 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1196 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1197 g_object_set_data( holder, "nodes_clippathedit", inky);
1198 }
1200 {
1201 InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1202 _("Edit mask path"),
1203 _("Edit the mask of the object"),
1204 "nodeedit-mask",
1205 Inkscape::ICON_SIZE_DECORATION );
1206 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1207 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1208 g_object_set_data( holder, "nodes_maskedit", inky);
1209 }
1211 /* X coord of selected node(s) */
1212 {
1213 EgeAdjustmentAction* eact = 0;
1214 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1215 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1216 eact = create_adjustment_action( "NodeXAction",
1217 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1218 "tools.nodes", "Xcoord", 0,
1219 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1220 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1221 labels, values, G_N_ELEMENTS(labels),
1222 sp_node_path_x_value_changed );
1223 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1224 g_object_set_data( holder, "nodes_x_action", eact );
1225 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1226 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1227 }
1229 /* Y coord of selected node(s) */
1230 {
1231 EgeAdjustmentAction* eact = 0;
1232 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1233 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1234 eact = create_adjustment_action( "NodeYAction",
1235 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1236 "tools.nodes", "Ycoord", 0,
1237 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1238 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1239 labels, values, G_N_ELEMENTS(labels),
1240 sp_node_path_y_value_changed );
1241 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1242 g_object_set_data( holder, "nodes_y_action", eact );
1243 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1244 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1245 }
1247 // add the units menu
1248 {
1249 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1250 gtk_action_group_add_action( mainActions, act );
1251 }
1254 sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1256 //watch selection
1257 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1259 sigc::connection *c_selection_changed =
1260 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1261 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1262 pool->add_connection ("selection-changed", c_selection_changed);
1264 sigc::connection *c_selection_modified =
1265 new sigc::connection (sp_desktop_selection (desktop)->connectModified
1266 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1267 pool->add_connection ("selection-modified", c_selection_modified);
1269 sigc::connection *c_subselection_changed =
1270 new sigc::connection (desktop->connectToolSubselectionChanged
1271 (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1272 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1274 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1276 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1277 } // end of sp_node_toolbox_prep()
1280 //########################
1281 //## Zoom Toolbox ##
1282 //########################
1284 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1285 {
1286 // no custom GtkAction setup needed
1287 } // end of sp_zoom_toolbox_prep()
1289 void
1290 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1291 {
1292 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")));
1293 }
1296 void
1297 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1298 {
1299 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)), desktop, setup_aux_toolbox, update_aux_toolbox, static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), "event_context_connection")));
1300 }
1302 void
1303 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1304 {
1305 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)), desktop, setup_commands_toolbox, update_commands_toolbox, static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), "event_context_connection")));
1306 }
1308 static void
1309 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1310 {
1311 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1312 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1314 if (old_desktop) {
1315 GList *children, *iter;
1317 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1318 for ( iter = children ; iter ; iter = iter->next ) {
1319 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1320 }
1321 g_list_free(children);
1322 }
1324 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1326 if (desktop) {
1327 gtk_widget_set_sensitive(toolbox, TRUE);
1328 setup_func(toolbox, desktop);
1329 update_func(desktop, desktop->event_context, toolbox);
1330 *conn = desktop->connectEventContextChanged
1331 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1332 } else {
1333 gtk_widget_set_sensitive(toolbox, FALSE);
1334 }
1336 } // end of toolbox_set_desktop()
1339 static void
1340 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1341 {
1342 GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1343 gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1344 if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1345 // "toolbox.tools" was not set. Fallback to older value
1346 shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1348 // Copy the setting forwards
1349 prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1350 }
1351 Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1353 for (int i = 0 ; tools[i].type_name ; i++ ) {
1354 GtkWidget *button =
1355 sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1356 SP_BUTTON_TYPE_TOGGLE,
1357 Inkscape::Verb::get(tools[i].verb),
1358 Inkscape::Verb::get(tools[i].doubleclick_verb),
1359 desktop,
1360 tooltips );
1362 g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1363 (gpointer)button );
1364 }
1365 }
1368 static void
1369 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1370 {
1371 gchar const *const tname = ( eventcontext
1372 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1373 : NULL );
1374 for (int i = 0 ; tools[i].type_name ; i++ ) {
1375 SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1376 sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1377 }
1378 }
1380 static void
1381 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1382 {
1383 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1384 GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1385 GtkUIManager* mgr = gtk_ui_manager_new();
1386 GError* errVal = 0;
1387 gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1388 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1390 std::map<std::string, GtkWidget*> dataHolders;
1392 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1393 if ( aux_toolboxes[i].prep_func ) {
1394 // converted to GtkActions and UIManager
1396 GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1397 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1398 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1399 dataHolders[aux_toolboxes[i].type_name] = kludge;
1400 aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1401 } else {
1403 GtkWidget *sub_toolbox = 0;
1404 if (aux_toolboxes[i].create_func == NULL)
1405 sub_toolbox = sp_empty_toolbox_new(desktop);
1406 else {
1407 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1408 }
1410 gtk_size_group_add_widget( grouper, sub_toolbox );
1412 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1413 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1415 }
1416 }
1418 // Second pass to create toolbars *after* all GtkActions are created
1419 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1420 if ( aux_toolboxes[i].prep_func ) {
1421 // converted to GtkActions and UIManager
1423 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1425 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1426 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1428 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1429 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1430 g_free( tmp );
1431 tmp = 0;
1433 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1434 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1435 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1436 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1437 }
1438 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1441 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1443 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1444 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1445 swatch->setDesktop( desktop );
1446 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1447 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1448 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1449 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 );
1450 }
1452 gtk_widget_show_all( holder );
1453 sp_set_font_size_smaller( holder );
1455 gtk_size_group_add_widget( grouper, holder );
1457 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1458 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1459 }
1460 }
1462 g_object_unref( G_OBJECT(grouper) );
1463 }
1465 static void
1466 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1467 {
1468 gchar const *tname = ( eventcontext
1469 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1470 : NULL );
1471 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1472 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1473 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1474 gtk_widget_show_all(sub_toolbox);
1475 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1476 } else {
1477 gtk_widget_hide(sub_toolbox);
1478 }
1479 }
1480 }
1482 static void
1483 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1484 {
1485 gchar const * descr =
1486 "<ui>"
1487 " <toolbar name='CommandsToolbar'>"
1488 " <toolitem action='FileNew' />"
1489 " <toolitem action='FileOpen' />"
1490 " <toolitem action='FileSave' />"
1491 " <toolitem action='FilePrint' />"
1492 " <separator />"
1493 " <toolitem action='FileImport' />"
1494 " <toolitem action='FileExport' />"
1495 " <separator />"
1496 " <toolitem action='EditUndo' />"
1497 " <toolitem action='EditRedo' />"
1498 " <separator />"
1499 " <toolitem action='EditCopy' />"
1500 " <toolitem action='EditCut' />"
1501 " <toolitem action='EditPaste' />"
1502 " <separator />"
1503 " <toolitem action='ZoomSelection' />"
1504 " <toolitem action='ZoomDrawing' />"
1505 " <toolitem action='ZoomPage' />"
1506 " <separator />"
1507 " <toolitem action='EditDuplicate' />"
1508 " <toolitem action='EditClone' />"
1509 " <toolitem action='EditUnlinkClone' />"
1510 " <separator />"
1511 " <toolitem action='SelectionGroup' />"
1512 " <toolitem action='SelectionUnGroup' />"
1513 " <separator />"
1514 " <toolitem action='DialogFillStroke' />"
1515 " <toolitem action='DialogText' />"
1516 " <toolitem action='DialogXMLEditor' />"
1517 " <toolitem action='DialogAlignDistribute' />"
1518 " <separator />"
1519 " <toolitem action='DialogPreferences' />"
1520 " <toolitem action='DialogDocumentProperties' />"
1521 " </toolbar>"
1522 "</ui>";
1523 GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1526 GtkUIManager* mgr = gtk_ui_manager_new();
1527 GError* errVal = 0;
1529 gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1530 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1532 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1533 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1534 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1535 }
1536 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1537 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1538 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1541 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1542 }
1544 static void
1545 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1546 {
1547 }
1549 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1550 {
1551 gtk_widget_show(toolbox_toplevel);
1552 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1554 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1555 if (!shown_toolbox) {
1556 return;
1557 }
1558 gtk_widget_show(toolbox);
1560 gtk_widget_show_all(shown_toolbox);
1561 }
1563 void
1564 aux_toolbox_space(GtkWidget *tb, gint space)
1565 {
1566 gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1567 }
1569 static GtkWidget *
1570 sp_empty_toolbox_new(SPDesktop *desktop)
1571 {
1572 GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1573 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1574 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1576 gtk_widget_show_all(tbl);
1577 sp_set_font_size_smaller (tbl);
1579 return tbl;
1580 }
1582 // helper UI functions
1584 GtkWidget *
1585 sp_tb_spinbutton(
1586 gchar *label, gchar const *tooltip,
1587 gchar const *path, gchar const *data, gdouble def,
1588 GtkWidget *us,
1589 GtkWidget *tbl,
1590 gboolean altx, gchar const *altx_mark,
1591 gdouble lower, gdouble upper, gdouble step, gdouble page,
1592 void (*callback)(GtkAdjustment *, GtkWidget *),
1593 gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1594 {
1595 GtkTooltips *tt = gtk_tooltips_new();
1597 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1599 GtkWidget *l = gtk_label_new(label);
1600 gtk_widget_show(l);
1601 gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1602 gtk_container_add(GTK_CONTAINER(hb), l);
1604 GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1605 lower, upper, step, page, page);
1606 gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1607 if (us)
1608 sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1610 GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1611 gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1612 if (altx)
1613 gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1614 gtk_widget_set_size_request(sb,
1615 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1616 AUX_SPINBUTTON_HEIGHT);
1617 gtk_widget_show(sb);
1618 gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1619 gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1620 gtk_container_add(GTK_CONTAINER(hb), sb);
1621 gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1623 return hb;
1624 }
1626 #define MODE_LABEL_WIDTH 70
1628 //########################
1629 //## Star ##
1630 //########################
1632 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1633 {
1634 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1636 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1637 // do not remember prefs if this call is initiated by an undo change, because undoing object
1638 // creation sets bogus values to its attributes before it is deleted
1639 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1640 }
1642 // quit if run by the attr_changed listener
1643 if (g_object_get_data( dataKludge, "freeze" )) {
1644 return;
1645 }
1647 // in turn, prevent listener from responding
1648 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1650 bool modmade = false;
1652 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1653 GSList const *items = selection->itemList();
1654 for (; items != NULL; items = items->next) {
1655 if (SP_IS_STAR((SPItem *) items->data)) {
1656 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1657 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1658 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1659 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1660 + M_PI / (gint)adj->value));
1661 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1662 modmade = true;
1663 }
1664 }
1665 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1666 _("Star: Change number of corners"));
1668 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1669 }
1671 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1672 {
1673 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1675 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1676 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1677 }
1679 // quit if run by the attr_changed listener
1680 if (g_object_get_data( dataKludge, "freeze" )) {
1681 return;
1682 }
1684 // in turn, prevent listener from responding
1685 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1687 bool modmade = false;
1688 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1689 GSList const *items = selection->itemList();
1690 for (; items != NULL; items = items->next) {
1691 if (SP_IS_STAR((SPItem *) items->data)) {
1692 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1694 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1695 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1696 if (r2 < r1) {
1697 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1698 } else {
1699 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1700 }
1702 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1703 modmade = true;
1704 }
1705 }
1707 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1708 _("Star: Change spoke ratio"));
1710 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1711 }
1713 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1714 {
1715 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1716 bool flat = ege_select_one_action_get_active( act ) == 0;
1718 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1719 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1720 flat ? "true" : "false" );
1721 }
1723 // quit if run by the attr_changed listener
1724 if (g_object_get_data( dataKludge, "freeze" )) {
1725 return;
1726 }
1728 // in turn, prevent listener from responding
1729 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1731 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1732 GSList const *items = selection->itemList();
1733 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1734 bool modmade = false;
1736 if ( prop_action ) {
1737 gtk_action_set_sensitive( prop_action, !flat );
1738 }
1740 for (; items != NULL; items = items->next) {
1741 if (SP_IS_STAR((SPItem *) items->data)) {
1742 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1743 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1744 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1745 modmade = true;
1746 }
1747 }
1749 if (modmade) {
1750 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1751 flat ? _("Make polygon") : _("Make star"));
1752 }
1754 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1755 }
1757 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1758 {
1759 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1761 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1762 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1763 }
1765 // quit if run by the attr_changed listener
1766 if (g_object_get_data( dataKludge, "freeze" )) {
1767 return;
1768 }
1770 // in turn, prevent listener from responding
1771 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1773 bool modmade = false;
1775 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1776 GSList const *items = selection->itemList();
1777 for (; items != NULL; items = items->next) {
1778 if (SP_IS_STAR((SPItem *) items->data)) {
1779 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1780 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1781 SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1782 modmade = true;
1783 }
1784 }
1785 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1786 _("Star: Change rounding"));
1788 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1789 }
1791 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1792 {
1793 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1795 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1796 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1797 }
1799 // quit if run by the attr_changed listener
1800 if (g_object_get_data( dataKludge, "freeze" )) {
1801 return;
1802 }
1804 // in turn, prevent listener from responding
1805 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1807 bool modmade = false;
1809 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1810 GSList const *items = selection->itemList();
1811 for (; items != NULL; items = items->next) {
1812 if (SP_IS_STAR((SPItem *) items->data)) {
1813 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1814 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1815 SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1816 modmade = true;
1817 }
1818 }
1819 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1820 _("Star: Change randomization"));
1822 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1823 }
1826 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1827 gchar const */*old_value*/, gchar const */*new_value*/,
1828 bool /*is_interactive*/, gpointer data)
1829 {
1830 GtkWidget *tbl = GTK_WIDGET(data);
1832 // quit if run by the _changed callbacks
1833 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1834 return;
1835 }
1837 // in turn, prevent callbacks from responding
1838 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1840 GtkAdjustment *adj = 0;
1842 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1843 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1845 if (!strcmp(name, "inkscape:randomized")) {
1846 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1847 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1848 } else if (!strcmp(name, "inkscape:rounded")) {
1849 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1850 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1851 } else if (!strcmp(name, "inkscape:flatsided")) {
1852 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1853 char const *flatsides = repr->attribute("inkscape:flatsided");
1854 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1855 if ( flatsides && !strcmp(flatsides,"false") ) {
1856 ege_select_one_action_set_active( flat_action, 1 );
1857 gtk_action_set_sensitive( prop_action, TRUE );
1858 } else {
1859 ege_select_one_action_set_active( flat_action, 0 );
1860 gtk_action_set_sensitive( prop_action, FALSE );
1861 }
1862 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1863 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1864 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1865 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1866 if (r2 < r1) {
1867 gtk_adjustment_set_value(adj, r2/r1);
1868 } else {
1869 gtk_adjustment_set_value(adj, r1/r2);
1870 }
1871 } else if (!strcmp(name, "sodipodi:sides")) {
1872 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1873 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1874 }
1876 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1877 }
1880 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1881 {
1882 NULL, /* child_added */
1883 NULL, /* child_removed */
1884 star_tb_event_attr_changed,
1885 NULL, /* content_changed */
1886 NULL /* order_changed */
1887 };
1890 /**
1891 * \param selection Should not be NULL.
1892 */
1893 static void
1894 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1895 {
1896 int n_selected = 0;
1897 Inkscape::XML::Node *repr = NULL;
1899 purge_repr_listener( tbl, tbl );
1901 for (GSList const *items = selection->itemList();
1902 items != NULL;
1903 items = items->next)
1904 {
1905 if (SP_IS_STAR((SPItem *) items->data)) {
1906 n_selected++;
1907 repr = SP_OBJECT_REPR((SPItem *) items->data);
1908 }
1909 }
1911 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1913 if (n_selected == 0) {
1914 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1915 } else if (n_selected == 1) {
1916 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1918 if (repr) {
1919 g_object_set_data( tbl, "repr", repr );
1920 Inkscape::GC::anchor(repr);
1921 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1922 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1923 }
1924 } else {
1925 // FIXME: implement averaging of all parameters for multiple selected stars
1926 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1927 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1928 }
1929 }
1932 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1933 {
1934 // FIXME: in this and all other _default functions, set some flag telling the value_changed
1935 // callbacks to lump all the changes for all selected objects in one undo step
1937 GtkAdjustment *adj = 0;
1939 // fixme: make settable in prefs!
1940 gint mag = 5;
1941 gdouble prop = 0.5;
1942 gboolean flat = FALSE;
1943 gdouble randomized = 0;
1944 gdouble rounded = 0;
1946 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1947 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1949 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1950 gtk_action_set_sensitive( sb2, !flat );
1952 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1953 gtk_adjustment_set_value(adj, mag);
1954 gtk_adjustment_value_changed(adj);
1956 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1957 gtk_adjustment_set_value(adj, prop);
1958 gtk_adjustment_value_changed(adj);
1960 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1961 gtk_adjustment_set_value(adj, rounded);
1962 gtk_adjustment_value_changed(adj);
1964 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1965 gtk_adjustment_set_value(adj, randomized);
1966 gtk_adjustment_value_changed(adj);
1967 }
1970 void
1971 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1972 {
1973 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1974 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1975 GtkWidget *l = gtk_label_new(NULL);
1976 gtk_label_set_markup(GTK_LABEL(l), title);
1977 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1978 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1979 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1980 }
1983 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1984 {
1985 {
1986 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1987 ege_output_action_set_use_markup( act, TRUE );
1988 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1989 g_object_set_data( holder, "mode_action", act );
1990 }
1992 {
1993 EgeAdjustmentAction* eact = 0;
1994 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1995 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1997 /* Flatsided checkbox */
1998 {
1999 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2001 GtkTreeIter iter;
2002 gtk_list_store_append( model, &iter );
2003 gtk_list_store_set( model, &iter,
2004 0, _("Polygon"),
2005 1, _("Regular polygon (with one handle) instead of a star"),
2006 2, "star_flat",
2007 -1 );
2009 gtk_list_store_append( model, &iter );
2010 gtk_list_store_set( model, &iter,
2011 0, _("Star"),
2012 1, _("Star instead of a regular polygon (with one handle)"),
2013 2, "star_angled",
2014 -1 );
2016 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2017 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2018 g_object_set_data( holder, "flat_action", act );
2020 ege_select_one_action_set_appearance( act, "full" );
2021 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2022 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2023 ege_select_one_action_set_icon_column( act, 2 );
2024 ege_select_one_action_set_tooltip_column( act, 1 );
2026 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2027 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2028 }
2030 /* Magnitude */
2031 {
2032 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2033 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2034 eact = create_adjustment_action( "MagnitudeAction",
2035 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2036 "tools.shapes.star", "magnitude", 3,
2037 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2038 3, 1024, 1, 5,
2039 labels, values, G_N_ELEMENTS(labels),
2040 sp_stb_magnitude_value_changed,
2041 1.0, 0 );
2042 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2043 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2044 }
2046 /* Spoke ratio */
2047 {
2048 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2049 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2050 eact = create_adjustment_action( "SpokeAction",
2051 _("Spoke ratio"), _("Spoke ratio:"),
2052 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2053 // Base radius is the same for the closest handle.
2054 _("Base radius to tip radius ratio"),
2055 "tools.shapes.star", "proportion", 0.5,
2056 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2057 0.01, 1.0, 0.01, 0.1,
2058 labels, values, G_N_ELEMENTS(labels),
2059 sp_stb_proportion_value_changed );
2060 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2061 g_object_set_data( holder, "prop_action", eact );
2062 }
2064 if ( !isFlatSided ) {
2065 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2066 } else {
2067 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2068 }
2070 /* Roundedness */
2071 {
2072 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2073 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2074 eact = create_adjustment_action( "RoundednessAction",
2075 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2076 "tools.shapes.star", "rounded", 0.0,
2077 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2078 -10.0, 10.0, 0.01, 0.1,
2079 labels, values, G_N_ELEMENTS(labels),
2080 sp_stb_rounded_value_changed );
2081 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2082 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2083 }
2085 /* Randomization */
2086 {
2087 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2088 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2089 eact = create_adjustment_action( "RandomizationAction",
2090 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2091 "tools.shapes.star", "randomized", 0.0,
2092 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2093 -10.0, 10.0, 0.001, 0.01,
2094 labels, values, G_N_ELEMENTS(labels),
2095 sp_stb_randomized_value_changed, 0.1, 3 );
2096 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2097 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2098 }
2099 }
2101 {
2102 /* Reset */
2103 {
2104 GtkAction* act = gtk_action_new( "StarResetAction",
2105 _("Defaults"),
2106 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2107 GTK_STOCK_CLEAR );
2108 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2109 gtk_action_group_add_action( mainActions, act );
2110 gtk_action_set_sensitive( act, TRUE );
2111 }
2112 }
2114 sigc::connection *connection = new sigc::connection(
2115 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2116 );
2117 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2118 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2119 }
2122 //########################
2123 //## Rect ##
2124 //########################
2126 static void sp_rtb_sensitivize( GObject *tbl )
2127 {
2128 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2129 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2130 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2132 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2133 gtk_action_set_sensitive( not_rounded, FALSE );
2134 } else {
2135 gtk_action_set_sensitive( not_rounded, TRUE );
2136 }
2137 }
2140 static void
2141 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2142 void (*setter)(SPRect *, gdouble))
2143 {
2144 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2146 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2147 SPUnit const *unit = tracker->getActiveUnit();
2149 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2150 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2151 }
2153 // quit if run by the attr_changed listener
2154 if (g_object_get_data( tbl, "freeze" )) {
2155 return;
2156 }
2158 // in turn, prevent listener from responding
2159 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2161 bool modmade = false;
2162 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2163 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2164 if (SP_IS_RECT(items->data)) {
2165 if (adj->value != 0) {
2166 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2167 } else {
2168 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2169 }
2170 modmade = true;
2171 }
2172 }
2174 sp_rtb_sensitivize( tbl );
2176 if (modmade) {
2177 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2178 _("Change rectangle"));
2179 }
2181 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2182 }
2184 static void
2185 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2186 {
2187 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2188 }
2190 static void
2191 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2192 {
2193 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2194 }
2196 static void
2197 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2198 {
2199 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2200 }
2202 static void
2203 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2204 {
2205 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2206 }
2210 static void
2211 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2212 {
2213 GtkAdjustment *adj = 0;
2215 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2216 gtk_adjustment_set_value(adj, 0.0);
2217 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2218 gtk_adjustment_value_changed(adj);
2220 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2221 gtk_adjustment_set_value(adj, 0.0);
2222 gtk_adjustment_value_changed(adj);
2224 sp_rtb_sensitivize( obj );
2225 }
2227 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2228 gchar const */*old_value*/, gchar const */*new_value*/,
2229 bool /*is_interactive*/, gpointer data)
2230 {
2231 GObject *tbl = G_OBJECT(data);
2233 // quit if run by the _changed callbacks
2234 if (g_object_get_data( tbl, "freeze" )) {
2235 return;
2236 }
2238 // in turn, prevent callbacks from responding
2239 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2241 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2242 SPUnit const *unit = tracker->getActiveUnit();
2244 gpointer item = g_object_get_data( tbl, "item" );
2245 if (item && SP_IS_RECT(item)) {
2246 {
2247 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2248 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2249 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2250 }
2252 {
2253 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2254 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2255 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2256 }
2258 {
2259 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2260 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2261 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2262 }
2264 {
2265 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2266 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2267 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2268 }
2269 }
2271 sp_rtb_sensitivize( tbl );
2273 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2274 }
2277 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2278 NULL, /* child_added */
2279 NULL, /* child_removed */
2280 rect_tb_event_attr_changed,
2281 NULL, /* content_changed */
2282 NULL /* order_changed */
2283 };
2285 /**
2286 * \param selection should not be NULL.
2287 */
2288 static void
2289 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2290 {
2291 int n_selected = 0;
2292 Inkscape::XML::Node *repr = NULL;
2293 SPItem *item = NULL;
2295 if ( g_object_get_data( tbl, "repr" ) ) {
2296 g_object_set_data( tbl, "item", NULL );
2297 }
2298 purge_repr_listener( tbl, tbl );
2300 for (GSList const *items = selection->itemList();
2301 items != NULL;
2302 items = items->next) {
2303 if (SP_IS_RECT((SPItem *) items->data)) {
2304 n_selected++;
2305 item = (SPItem *) items->data;
2306 repr = SP_OBJECT_REPR(item);
2307 }
2308 }
2310 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2312 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2314 if (n_selected == 0) {
2315 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2317 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2318 gtk_action_set_sensitive(w, FALSE);
2319 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2320 gtk_action_set_sensitive(h, FALSE);
2322 } else if (n_selected == 1) {
2323 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2324 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2326 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2327 gtk_action_set_sensitive(w, TRUE);
2328 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2329 gtk_action_set_sensitive(h, TRUE);
2331 if (repr) {
2332 g_object_set_data( tbl, "repr", repr );
2333 g_object_set_data( tbl, "item", item );
2334 Inkscape::GC::anchor(repr);
2335 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2336 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2337 }
2338 } else {
2339 // FIXME: implement averaging of all parameters for multiple selected
2340 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2341 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2342 sp_rtb_sensitivize( tbl );
2343 }
2344 }
2347 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2348 {
2349 EgeAdjustmentAction* eact = 0;
2351 {
2352 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2353 ege_output_action_set_use_markup( act, TRUE );
2354 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2355 g_object_set_data( holder, "mode_action", act );
2356 }
2358 // rx/ry units menu: create
2359 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2360 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2361 // fixme: add % meaning per cent of the width/height
2362 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2363 g_object_set_data( holder, "tracker", tracker );
2365 /* W */
2366 {
2367 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2368 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2369 eact = create_adjustment_action( "RectWidthAction",
2370 _("Width"), _("W:"), _("Width of rectangle"),
2371 "tools.shapes.rect", "width", 0,
2372 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2373 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2374 labels, values, G_N_ELEMENTS(labels),
2375 sp_rtb_width_value_changed );
2376 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2377 g_object_set_data( holder, "width_action", eact );
2378 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2379 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2380 }
2382 /* H */
2383 {
2384 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2385 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2386 eact = create_adjustment_action( "RectHeightAction",
2387 _("Height"), _("H:"), _("Height of rectangle"),
2388 "tools.shapes.rect", "height", 0,
2389 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2390 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2391 labels, values, G_N_ELEMENTS(labels),
2392 sp_rtb_height_value_changed );
2393 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2394 g_object_set_data( holder, "height_action", eact );
2395 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2396 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2397 }
2399 /* rx */
2400 {
2401 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2402 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2403 eact = create_adjustment_action( "RadiusXAction",
2404 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2405 "tools.shapes.rect", "rx", 0,
2406 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2407 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2408 labels, values, G_N_ELEMENTS(labels),
2409 sp_rtb_rx_value_changed);
2410 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2411 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2412 }
2414 /* ry */
2415 {
2416 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2417 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2418 eact = create_adjustment_action( "RadiusYAction",
2419 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2420 "tools.shapes.rect", "ry", 0,
2421 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2422 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2423 labels, values, G_N_ELEMENTS(labels),
2424 sp_rtb_ry_value_changed);
2425 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2426 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2427 }
2429 // add the units menu
2430 {
2431 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2432 gtk_action_group_add_action( mainActions, act );
2433 }
2435 /* Reset */
2436 {
2437 InkAction* inky = ink_action_new( "RectResetAction",
2438 _("Not rounded"),
2439 _("Make corners sharp"),
2440 "squared_corner",
2441 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2442 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2443 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2444 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2445 g_object_set_data( holder, "not_rounded", inky );
2446 }
2448 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2449 sp_rtb_sensitivize( holder );
2451 sigc::connection *connection = new sigc::connection(
2452 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2453 );
2454 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2455 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2456 }
2458 //########################
2459 //## 3D Box ##
2460 //########################
2462 // normalize angle so that it lies in the interval [0,360]
2463 static double box3d_normalize_angle (double a) {
2464 double angle = a + ((int) (a/360.0))*360;
2465 if (angle < 0) {
2466 angle += 360.0;
2467 }
2468 return angle;
2469 }
2471 static void
2472 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2473 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2474 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2475 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2476 // are reset).
2477 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2479 if (is_infinite) {
2480 gtk_toggle_action_set_active(tact, TRUE);
2481 gtk_action_set_sensitive(act, TRUE);
2483 double angle = persp3d_get_infinite_angle(persp, axis);
2484 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2485 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2486 }
2487 } else {
2488 gtk_toggle_action_set_active(tact, FALSE);
2489 gtk_action_set_sensitive(act, FALSE);
2490 }
2491 }
2493 static void
2494 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2495 if (!persp_repr) {
2496 g_print ("No perspective given to box3d_resync_toolbar().\n");
2497 return;
2498 }
2500 GtkWidget *tbl = GTK_WIDGET(data);
2501 GtkAdjustment *adj = 0;
2502 GtkAction *act = 0;
2503 GtkToggleAction *tact = 0;
2504 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2505 {
2506 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2507 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2508 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2510 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2511 }
2512 {
2513 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2514 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2515 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2517 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2518 }
2519 {
2520 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2521 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2522 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2524 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2525 }
2526 }
2528 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2529 gchar const */*old_value*/, gchar const */*new_value*/,
2530 bool /*is_interactive*/, gpointer data)
2531 {
2532 GtkWidget *tbl = GTK_WIDGET(data);
2534 // quit if run by the attr_changed listener
2535 // note: it used to work without the differently called freeze_ attributes (here and in
2536 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2537 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2538 return;
2539 }
2541 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2542 // sp_document_maybe_done() when the document is undo insensitive)
2543 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2545 // TODO: Only update the appropriate part of the toolbar
2546 // if (!strcmp(name, "inkscape:vp_z")) {
2547 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2548 // }
2550 Persp3D *persp = persp3d_get_from_repr(repr);
2551 persp3d_update_box_reprs(persp);
2553 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2554 }
2556 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2557 {
2558 NULL, /* child_added */
2559 NULL, /* child_removed */
2560 box3d_persp_tb_event_attr_changed,
2561 NULL, /* content_changed */
2562 NULL /* order_changed */
2563 };
2565 /**
2566 * \param selection Should not be NULL.
2567 */
2568 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2569 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2570 static void
2571 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2572 {
2573 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2574 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2575 // update the perspectives with infinite VPs and leave the other ones untouched).
2577 Inkscape::XML::Node *persp_repr = NULL;
2578 purge_repr_listener(tbl, tbl);
2580 SPItem *item = selection->singleItem();
2581 if (item && SP_IS_BOX3D(item)) {
2582 // FIXME: Also deal with multiple selected boxes
2583 SPBox3D *box = SP_BOX3D(item);
2584 Persp3D *persp = box3d_get_perspective(box);
2585 persp_repr = SP_OBJECT_REPR(persp);
2586 if (persp_repr) {
2587 g_object_set_data(tbl, "repr", persp_repr);
2588 Inkscape::GC::anchor(persp_repr);
2589 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2590 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2591 }
2593 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2594 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2596 box3d_resync_toolbar(persp_repr, tbl);
2597 }
2598 }
2600 static void
2601 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2602 {
2603 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2604 SPDocument *document = sp_desktop_document(desktop);
2606 // quit if run by the attr_changed listener
2607 // note: it used to work without the differently called freeze_ attributes (here and in
2608 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2609 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2610 return;
2611 }
2613 // in turn, prevent listener from responding
2614 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2616 //Persp3D *persp = document->current_persp3d;
2617 std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps();
2618 if (sel_persps.empty()) {
2619 // this can happen when the document is created; we silently ignore it
2620 return;
2621 }
2622 Persp3D *persp = *(sel_persps.begin());
2624 persp->tmat.set_infinite_direction (axis, adj->value);
2625 SP_OBJECT(persp)->updateRepr();
2627 // TODO: use the correct axis here, too
2628 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2630 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2631 }
2634 static void
2635 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2636 {
2637 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2638 }
2640 static void
2641 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2642 {
2643 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2644 }
2646 static void
2647 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2648 {
2649 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2650 }
2653 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2654 {
2655 // TODO: Take all selected perspectives into account
2656 std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps();
2657 if (sel_persps.empty()) {
2658 // this can happen when the document is created; we silently ignore it
2659 return;
2660 }
2661 Persp3D *persp = *(sel_persps.begin());
2663 bool set_infinite = gtk_toggle_action_get_active(act);
2664 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2665 }
2667 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2668 {
2669 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2670 }
2672 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2673 {
2674 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2675 }
2677 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2678 {
2679 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2680 }
2682 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2683 {
2684 EgeAdjustmentAction* eact = 0;
2685 SPDocument *document = sp_desktop_document (desktop);
2686 Persp3D *persp = document->current_persp3d;
2688 EgeAdjustmentAction* box3d_angle_x = 0;
2689 EgeAdjustmentAction* box3d_angle_y = 0;
2690 EgeAdjustmentAction* box3d_angle_z = 0;
2692 /* Angle X */
2693 {
2694 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2695 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2696 eact = create_adjustment_action( "3DBoxAngleXAction",
2697 _("Angle in X direction"), _("Angle X:"),
2698 // Translators: PL is short for 'perspective line'
2699 _("Angle of PLs in X direction"),
2700 "tools.shapes.3dbox", "box3d_angle_x", 30,
2701 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2702 -360.0, 360.0, 1.0, 10.0,
2703 labels, values, G_N_ELEMENTS(labels),
2704 box3d_angle_x_value_changed );
2705 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2706 g_object_set_data( holder, "box3d_angle_x_action", eact );
2707 box3d_angle_x = eact;
2708 }
2710 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2711 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2712 } else {
2713 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2714 }
2717 /* VP X state */
2718 {
2719 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2720 // Translators: VP is short for 'vanishing point'
2721 _("State of VP in X direction"),
2722 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2723 "toggle_vp_x",
2724 Inkscape::ICON_SIZE_DECORATION );
2725 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2726 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2727 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2728 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2729 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2730 }
2732 /* Angle Y */
2733 {
2734 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2735 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2736 eact = create_adjustment_action( "3DBoxAngleYAction",
2737 _("Angle in Y direction"), _("Angle Y:"),
2738 // Translators: PL is short for 'perspective line'
2739 _("Angle of PLs in Y direction"),
2740 "tools.shapes.3dbox", "box3d_angle_y", 30,
2741 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2742 -360.0, 360.0, 1.0, 10.0,
2743 labels, values, G_N_ELEMENTS(labels),
2744 box3d_angle_y_value_changed );
2745 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2746 g_object_set_data( holder, "box3d_angle_y_action", eact );
2747 box3d_angle_y = eact;
2748 }
2750 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2751 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2752 } else {
2753 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2754 }
2756 /* VP Y state */
2757 {
2758 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2759 // Translators: VP is short for 'vanishing point'
2760 _("State of VP in Y direction"),
2761 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2762 "toggle_vp_y",
2763 Inkscape::ICON_SIZE_DECORATION );
2764 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2765 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2766 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2767 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2768 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2769 }
2771 /* Angle Z */
2772 {
2773 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2774 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2775 eact = create_adjustment_action( "3DBoxAngleZAction",
2776 _("Angle in Z direction"), _("Angle Z:"),
2777 // Translators: PL is short for 'perspective line'
2778 _("Angle of PLs in Z direction"),
2779 "tools.shapes.3dbox", "box3d_angle_z", 30,
2780 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2781 -360.0, 360.0, 1.0, 10.0,
2782 labels, values, G_N_ELEMENTS(labels),
2783 box3d_angle_z_value_changed );
2784 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2785 g_object_set_data( holder, "box3d_angle_z_action", eact );
2786 box3d_angle_z = eact;
2787 }
2789 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2790 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2791 } else {
2792 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2793 }
2795 /* VP Z state */
2796 {
2797 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2798 // Translators: VP is short for 'vanishing point'
2799 _("State of VP in Z direction"),
2800 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2801 "toggle_vp_z",
2802 Inkscape::ICON_SIZE_DECORATION );
2803 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2804 g_object_set_data( holder, "box3d_vp_z_state_action", act );
2805 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2806 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2807 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2808 }
2810 sigc::connection *connection = new sigc::connection(
2811 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2812 );
2813 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2814 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2815 }
2817 //########################
2818 //## Spiral ##
2819 //########################
2821 static void
2822 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2823 {
2824 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2826 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2827 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2828 }
2830 // quit if run by the attr_changed listener
2831 if (g_object_get_data( tbl, "freeze" )) {
2832 return;
2833 }
2835 // in turn, prevent listener from responding
2836 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2838 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2840 bool modmade = false;
2841 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2842 items != NULL;
2843 items = items->next)
2844 {
2845 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2846 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2847 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2848 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2849 modmade = true;
2850 }
2851 }
2853 g_free(namespaced_name);
2855 if (modmade) {
2856 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2857 _("Change spiral"));
2858 }
2860 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2861 }
2863 static void
2864 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2865 {
2866 sp_spl_tb_value_changed(adj, tbl, "revolution");
2867 }
2869 static void
2870 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2871 {
2872 sp_spl_tb_value_changed(adj, tbl, "expansion");
2873 }
2875 static void
2876 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2877 {
2878 sp_spl_tb_value_changed(adj, tbl, "t0");
2879 }
2881 static void
2882 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2883 {
2884 GtkWidget *tbl = GTK_WIDGET(obj);
2886 GtkAdjustment *adj;
2888 // fixme: make settable
2889 gdouble rev = 5;
2890 gdouble exp = 1.0;
2891 gdouble t0 = 0.0;
2893 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2894 gtk_adjustment_set_value(adj, rev);
2895 gtk_adjustment_value_changed(adj);
2897 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2898 gtk_adjustment_set_value(adj, exp);
2899 gtk_adjustment_value_changed(adj);
2901 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2902 gtk_adjustment_set_value(adj, t0);
2903 gtk_adjustment_value_changed(adj);
2905 spinbutton_defocus(GTK_OBJECT(tbl));
2906 }
2909 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2910 gchar const */*old_value*/, gchar const */*new_value*/,
2911 bool /*is_interactive*/, gpointer data)
2912 {
2913 GtkWidget *tbl = GTK_WIDGET(data);
2915 // quit if run by the _changed callbacks
2916 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2917 return;
2918 }
2920 // in turn, prevent callbacks from responding
2921 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2923 GtkAdjustment *adj;
2924 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2925 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2927 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2928 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2930 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2931 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2933 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2934 }
2937 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2938 NULL, /* child_added */
2939 NULL, /* child_removed */
2940 spiral_tb_event_attr_changed,
2941 NULL, /* content_changed */
2942 NULL /* order_changed */
2943 };
2945 static void
2946 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2947 {
2948 int n_selected = 0;
2949 Inkscape::XML::Node *repr = NULL;
2951 purge_repr_listener( tbl, tbl );
2953 for (GSList const *items = selection->itemList();
2954 items != NULL;
2955 items = items->next)
2956 {
2957 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2958 n_selected++;
2959 repr = SP_OBJECT_REPR((SPItem *) items->data);
2960 }
2961 }
2963 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2965 if (n_selected == 0) {
2966 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2967 } else if (n_selected == 1) {
2968 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2970 if (repr) {
2971 g_object_set_data( tbl, "repr", repr );
2972 Inkscape::GC::anchor(repr);
2973 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2974 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2975 }
2976 } else {
2977 // FIXME: implement averaging of all parameters for multiple selected
2978 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2979 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2980 }
2981 }
2984 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2985 {
2986 EgeAdjustmentAction* eact = 0;
2988 {
2989 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2990 ege_output_action_set_use_markup( act, TRUE );
2991 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2992 g_object_set_data( holder, "mode_action", act );
2993 }
2995 /* Revolution */
2996 {
2997 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2998 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2999 eact = create_adjustment_action( "SpiralRevolutionAction",
3000 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3001 "tools.shapes.spiral", "revolution", 3.0,
3002 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3003 0.01, 1024.0, 0.1, 1.0,
3004 labels, values, G_N_ELEMENTS(labels),
3005 sp_spl_tb_revolution_value_changed, 1, 2);
3006 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3007 }
3009 /* Expansion */
3010 {
3011 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3012 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3013 eact = create_adjustment_action( "SpiralExpansionAction",
3014 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3015 "tools.shapes.spiral", "expansion", 1.0,
3016 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3017 0.0, 1000.0, 0.01, 1.0,
3018 labels, values, G_N_ELEMENTS(labels),
3019 sp_spl_tb_expansion_value_changed);
3020 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3021 }
3023 /* T0 */
3024 {
3025 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3026 gdouble values[] = {0, 0.5, 0.9};
3027 eact = create_adjustment_action( "SpiralT0Action",
3028 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3029 "tools.shapes.spiral", "t0", 0.0,
3030 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3031 0.0, 0.999, 0.01, 1.0,
3032 labels, values, G_N_ELEMENTS(labels),
3033 sp_spl_tb_t0_value_changed);
3034 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3035 }
3037 /* Reset */
3038 {
3039 InkAction* inky = ink_action_new( "SpiralResetAction",
3040 _("Defaults"),
3041 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3042 GTK_STOCK_CLEAR,
3043 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3044 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3045 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3046 }
3049 sigc::connection *connection = new sigc::connection(
3050 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3051 );
3052 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3053 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3054 }
3056 //########################
3057 //## Pen/Pencil ##
3058 //########################
3061 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
3062 {
3063 // Put stuff here
3064 }
3066 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
3067 {
3068 // Put stuff here
3069 }
3071 //########################
3072 //## Tweak ##
3073 //########################
3075 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3076 {
3077 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3078 }
3080 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3081 {
3082 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3083 }
3085 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3086 {
3087 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3088 }
3090 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3091 {
3092 int mode = ege_select_one_action_get_active( act );
3093 prefs_set_int_attribute("tools.tweak", "mode", mode);
3095 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3096 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3097 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3098 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3099 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3100 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3101 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3102 if (doh) gtk_action_set_sensitive (doh, TRUE);
3103 if (dos) gtk_action_set_sensitive (dos, TRUE);
3104 if (dol) gtk_action_set_sensitive (dol, TRUE);
3105 if (doo) gtk_action_set_sensitive (doo, TRUE);
3106 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3107 if (fid) gtk_action_set_sensitive (fid, FALSE);
3108 } else {
3109 if (doh) gtk_action_set_sensitive (doh, FALSE);
3110 if (dos) gtk_action_set_sensitive (dos, FALSE);
3111 if (dol) gtk_action_set_sensitive (dol, FALSE);
3112 if (doo) gtk_action_set_sensitive (doo, FALSE);
3113 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3114 if (fid) gtk_action_set_sensitive (fid, TRUE);
3115 }
3116 }
3118 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3119 {
3120 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3121 }
3123 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3124 bool show = gtk_toggle_action_get_active( act );
3125 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3126 }
3127 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3128 bool show = gtk_toggle_action_get_active( act );
3129 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3130 }
3131 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3132 bool show = gtk_toggle_action_get_active( act );
3133 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3134 }
3135 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3136 bool show = gtk_toggle_action_get_active( act );
3137 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3138 }
3140 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3141 {
3142 {
3143 /* Width */
3144 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3145 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3146 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3147 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3148 "tools.tweak", "width", 15,
3149 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3150 1, 100, 1.0, 10.0,
3151 labels, values, G_N_ELEMENTS(labels),
3152 sp_tweak_width_value_changed, 0.01, 0, 100 );
3153 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3154 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3155 }
3158 {
3159 /* Force */
3160 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3161 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3162 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3163 _("Force"), _("Force:"), _("The force of the tweak action"),
3164 "tools.tweak", "force", 20,
3165 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3166 1, 100, 1.0, 10.0,
3167 labels, values, G_N_ELEMENTS(labels),
3168 sp_tweak_force_value_changed, 0.01, 0, 100 );
3169 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3170 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3171 }
3173 /* Mode */
3174 {
3175 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3177 GtkTreeIter iter;
3178 gtk_list_store_append( model, &iter );
3179 gtk_list_store_set( model, &iter,
3180 0, _("Push mode"),
3181 1, _("Push parts of paths in any direction"),
3182 2, "tweak_push_mode",
3183 -1 );
3185 gtk_list_store_append( model, &iter );
3186 gtk_list_store_set( model, &iter,
3187 0, _("Shrink mode"),
3188 1, _("Shrink (inset) parts of paths"),
3189 2, "tweak_shrink_mode",
3190 -1 );
3192 gtk_list_store_append( model, &iter );
3193 gtk_list_store_set( model, &iter,
3194 0, _("Grow mode"),
3195 1, _("Grow (outset) parts of paths"),
3196 2, "tweak_grow_mode",
3197 -1 );
3199 gtk_list_store_append( model, &iter );
3200 gtk_list_store_set( model, &iter,
3201 0, _("Attract mode"),
3202 1, _("Attract parts of paths towards cursor"),
3203 2, "tweak_attract_mode",
3204 -1 );
3206 gtk_list_store_append( model, &iter );
3207 gtk_list_store_set( model, &iter,
3208 0, _("Repel mode"),
3209 1, _("Repel parts of paths from cursor"),
3210 2, "tweak_repel_mode",
3211 -1 );
3213 gtk_list_store_append( model, &iter );
3214 gtk_list_store_set( model, &iter,
3215 0, _("Roughen mode"),
3216 1, _("Roughen parts of paths"),
3217 2, "tweak_roughen_mode",
3218 -1 );
3220 gtk_list_store_append( model, &iter );
3221 gtk_list_store_set( model, &iter,
3222 0, _("Color paint mode"),
3223 1, _("Paint the tool's color upon selected objects"),
3224 2, "tweak_colorpaint_mode",
3225 -1 );
3227 gtk_list_store_append( model, &iter );
3228 gtk_list_store_set( model, &iter,
3229 0, _("Color jitter mode"),
3230 1, _("Jitter the colors of selected objects"),
3231 2, "tweak_colorjitter_mode",
3232 -1 );
3234 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3235 g_object_set( act, "short_label", _("Mode:"), NULL );
3236 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3237 g_object_set_data( holder, "mode_action", act );
3239 ege_select_one_action_set_appearance( act, "full" );
3240 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3241 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3242 ege_select_one_action_set_icon_column( act, 2 );
3243 ege_select_one_action_set_tooltip_column( act, 1 );
3245 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3246 ege_select_one_action_set_active( act, mode );
3247 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3249 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3250 }
3252 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3254 {
3255 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3256 ege_output_action_set_use_markup( act, TRUE );
3257 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3258 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3259 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3260 g_object_set_data( holder, "tweak_channels_label", act);
3261 }
3263 {
3264 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3265 _("Hue"),
3266 _("In color mode, act on objects' hue"),
3267 NULL,
3268 Inkscape::ICON_SIZE_DECORATION );
3269 g_object_set( act, "short_label", _("H"), NULL );
3270 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3271 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3272 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3273 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3274 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3275 g_object_set_data( holder, "tweak_doh", act);
3276 }
3277 {
3278 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3279 _("Saturation"),
3280 _("In color mode, act on objects' saturation"),
3281 NULL,
3282 Inkscape::ICON_SIZE_DECORATION );
3283 g_object_set( act, "short_label", _("S"), NULL );
3284 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3285 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3286 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3287 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3288 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3289 g_object_set_data( holder, "tweak_dos", act );
3290 }
3291 {
3292 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3293 _("Lightness"),
3294 _("In color mode, act on objects' lightness"),
3295 NULL,
3296 Inkscape::ICON_SIZE_DECORATION );
3297 g_object_set( act, "short_label", _("L"), NULL );
3298 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3299 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3300 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3301 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3302 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3303 g_object_set_data( holder, "tweak_dol", act );
3304 }
3305 {
3306 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3307 _("Opacity"),
3308 _("In color mode, act on objects' opacity"),
3309 NULL,
3310 Inkscape::ICON_SIZE_DECORATION );
3311 g_object_set( act, "short_label", _("O"), NULL );
3312 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3313 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3314 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3315 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3316 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3317 g_object_set_data( holder, "tweak_doo", act );
3318 }
3320 { /* Fidelity */
3321 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3322 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3323 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3324 _("Fidelity"), _("Fidelity:"),
3325 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3326 "tools.tweak", "fidelity", 50,
3327 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3328 1, 100, 1.0, 10.0,
3329 labels, values, G_N_ELEMENTS(labels),
3330 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3331 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3332 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3333 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3334 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3335 g_object_set_data( holder, "tweak_fidelity", eact );
3336 }
3339 /* Use Pressure button */
3340 {
3341 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3342 _("Pressure"),
3343 _("Use the pressure of the input device to alter the force of tweak action"),
3344 "use_pressure",
3345 Inkscape::ICON_SIZE_DECORATION );
3346 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3347 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3348 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3349 }
3351 }
3354 //########################
3355 //## Calligraphy ##
3356 //########################
3358 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3359 {
3360 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3361 }
3363 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3364 {
3365 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3366 }
3368 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3369 {
3370 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3371 }
3373 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3374 {
3375 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3376 }
3378 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3379 {
3380 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3381 }
3383 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3384 {
3385 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3386 }
3388 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3389 {
3390 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3391 }
3393 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3394 {
3395 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3396 }
3398 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3399 {
3400 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3401 }
3403 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3404 {
3405 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3406 }
3408 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3409 {
3410 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3412 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3413 }
3415 struct KeyValue {
3416 char const *key;
3417 double value;
3418 };
3422 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject *dataKludge) {
3423 struct ProfileElement {
3424 char const *name;
3425 double def;
3426 double min;
3427 double max;
3428 };
3429 ProfileElement profile[] = {
3430 {"mass",0.02, 0.0, 1.0},
3431 {"wiggle",0.0, 0.0, 1.0},
3432 {"angle",30.0, -90.0, 90.0},
3433 {"width",15.0, 1.0, 100.0},
3434 {"thinning",0.1, -1.0, 1.0},
3435 {"tremor",0.0, 0.0, 1.0},
3436 {"flatness",0.9, 0.0, 1.0},
3437 {"cap_rounding",0.0, 0.0, 5.0}
3438 };
3440 gint preset_index = ege_select_one_action_get_active( act );
3441 gchar *pref_path = g_strdup_printf("tools.calligraphic.preset.cp%d", preset_index);
3442 for (unsigned i = 0; i < 8; ++i) {
3443 ProfileElement const &pe = profile[i];
3444 double value = prefs_get_double_attribute_limited(pref_path,pe.name, pe.def, pe.min, pe.max);
3445 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, pe.name));
3446 if ( adj ) {
3447 gtk_adjustment_set_value(adj, value);
3448 }
3449 }
3450 free(pref_path);
3452 }
3455 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3456 {
3457 {
3458 EgeAdjustmentAction* calligraphy_angle = 0;
3460 {
3461 /* Width */
3462 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3463 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3464 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3465 _("Pen Width"), _("Width:"),
3466 _("The width of the calligraphic pen (relative to the visible canvas area)"),
3467 "tools.calligraphic", "width", 15,
3468 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3469 1, 100, 1.0, 10.0,
3470 labels, values, G_N_ELEMENTS(labels),
3471 sp_ddc_width_value_changed, 0.01, 0, 100 );
3472 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3473 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3474 }
3476 {
3477 /* Thinning */
3478 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3479 gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3480 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3481 _("Stroke Thinning"), _("Thinning:"),
3482 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3483 "tools.calligraphic", "thinning", 0.1,
3484 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3485 -1.0, 1.0, 0.01, 0.1,
3486 labels, values, G_N_ELEMENTS(labels),
3487 sp_ddc_velthin_value_changed, 0.01, 2);
3488 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3489 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3490 }
3492 {
3493 /* Angle */
3494 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3495 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3496 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3497 _("Pen Angle"), _("Angle:"),
3498 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3499 "tools.calligraphic", "angle", 30,
3500 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3501 -90.0, 90.0, 1.0, 10.0,
3502 labels, values, G_N_ELEMENTS(labels),
3503 sp_ddc_angle_value_changed, 1, 0 );
3504 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3505 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3506 calligraphy_angle = eact;
3507 }
3509 {
3510 /* Fixation */
3511 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3512 gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3513 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3514 _("Fixation"), _("Fixation:"),
3515 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3516 "tools.calligraphic", "flatness", 0.9,
3517 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3518 0.0, 1.0, 0.01, 0.1,
3519 labels, values, G_N_ELEMENTS(labels),
3520 sp_ddc_flatness_value_changed, 0.01, 2 );
3521 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3522 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3523 }
3525 {
3526 /* Cap Rounding */
3527 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3528 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3529 // TRANSLATORS: "cap" means "end" (both start and finish) here
3530 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3531 _("Cap rounding"), _("Caps:"),
3532 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3533 "tools.calligraphic", "cap_rounding", 0.0,
3534 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3535 0.0, 5.0, 0.01, 0.1,
3536 labels, values, G_N_ELEMENTS(labels),
3537 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3538 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3539 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3540 }
3542 {
3543 /* Tremor */
3544 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3545 gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3546 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3547 _("Stroke Tremor"), _("Tremor:"),
3548 _("Increase to make strokes rugged and trembling"),
3549 "tools.calligraphic", "tremor", 0.0,
3550 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3551 0.0, 1.0, 0.01, 0.1,
3552 labels, values, G_N_ELEMENTS(labels),
3553 sp_ddc_tremor_value_changed, 0.01, 2 );
3555 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3556 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3557 }
3559 {
3560 /* Wiggle */
3561 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3562 gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3563 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3564 _("Pen Wiggle"), _("Wiggle:"),
3565 _("Increase to make the pen waver and wiggle"),
3566 "tools.calligraphic", "wiggle", 0.0,
3567 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3568 0.0, 1.0, 0.01, 0.1,
3569 labels, values, G_N_ELEMENTS(labels),
3570 sp_ddc_wiggle_value_changed, 0.01, 2 );
3571 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3572 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3573 }
3575 {
3576 /* Mass */
3577 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3578 gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3579 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3580 _("Pen Mass"), _("Mass:"),
3581 _("Increase to make the pen drag behind, as if slowed by inertia"),
3582 "tools.calligraphic", "mass", 0.02,
3583 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3584 0.0, 1.0, 0.01, 0.1,
3585 labels, values, G_N_ELEMENTS(labels),
3586 sp_ddc_mass_value_changed, 0.01, 2 );
3587 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3588 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3589 }
3592 /* Trace Background button */
3593 {
3594 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3595 _("Trace Background"),
3596 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3597 "trace_background",
3598 Inkscape::ICON_SIZE_DECORATION );
3599 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3600 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3601 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3602 }
3604 /* Use Pressure button */
3605 {
3606 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3607 _("Pressure"),
3608 _("Use the pressure of the input device to alter the width of the pen"),
3609 "use_pressure",
3610 Inkscape::ICON_SIZE_DECORATION );
3611 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3612 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3613 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3614 }
3616 /* Use Tilt button */
3617 {
3618 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3619 _("Tilt"),
3620 _("Use the tilt of the input device to alter the angle of the pen's nib"),
3621 "use_tilt",
3622 Inkscape::ICON_SIZE_DECORATION );
3623 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3624 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3625 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3626 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3627 }
3629 /*calligraphic profile */
3630 {
3631 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3632 gchar *pref_path;
3634 int max = prefs_get_int_attribute("tools.calligraphic.preset","max_presets",99);
3635 for ( int ii = 0; ii < max; ++ii){
3636 pref_path = g_strdup_printf("tools.calligraphic.preset.cp%d", ii);
3638 if ( ! pref_path_exists(pref_path)){
3639 free(pref_path );
3640 break;
3641 }
3643 gchar const *preset_name = prefs_get_string_attribute(pref_path,"name");
3644 GtkTreeIter iter;
3645 gtk_list_store_append( model, &iter );
3646 gtk_list_store_set( model, &iter, 0, preset_name, 1, ii, -1 );
3647 free(pref_path );
3648 }
3649 pref_path = NULL;
3650 EgeSelectOneAction* act1 = ege_select_one_action_new( "SetProfileAction", _("Profile"), ("Change calligraphic profile"), NULL, GTK_TREE_MODEL(model) );
3651 g_object_set( act1, "short_label", _("Profile:"), NULL );
3652 ege_select_one_action_set_appearance( act1, "compact" );
3653 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder );
3654 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3655 g_object_set_data( holder, "channels_action", act1 );
3656 }
3657 }
3658 }
3661 //########################
3662 //## Circle / Arc ##
3663 //########################
3665 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3666 {
3667 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3668 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3670 if (v1 == 0 && v2 == 0) {
3671 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3672 gtk_action_set_sensitive( ocb, FALSE );
3673 gtk_action_set_sensitive( make_whole, FALSE );
3674 }
3675 } else {
3676 gtk_action_set_sensitive( ocb, TRUE );
3677 gtk_action_set_sensitive( make_whole, TRUE );
3678 }
3679 }
3681 static void
3682 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3683 {
3684 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3686 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3687 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3688 }
3690 // quit if run by the attr_changed listener
3691 if (g_object_get_data( tbl, "freeze" )) {
3692 return;
3693 }
3695 // in turn, prevent listener from responding
3696 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3698 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3700 bool modmade = false;
3701 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3702 items != NULL;
3703 items = items->next)
3704 {
3705 SPItem *item = SP_ITEM(items->data);
3707 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3709 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3710 SPArc *arc = SP_ARC(item);
3712 if (!strcmp(value_name, "start"))
3713 ge->start = (adj->value * M_PI)/ 180;
3714 else
3715 ge->end = (adj->value * M_PI)/ 180;
3717 sp_genericellipse_normalize(ge);
3718 ((SPObject *)arc)->updateRepr();
3719 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3721 modmade = true;
3722 }
3723 }
3725 g_free(namespaced_name);
3727 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3729 sp_arctb_sensitivize( tbl, adj->value, other->value );
3731 if (modmade) {
3732 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3733 _("Arc: Change start/end"));
3734 }
3736 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3737 }
3740 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
3741 {
3742 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
3743 }
3745 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3746 {
3747 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
3748 }
3750 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3751 {
3752 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3753 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3754 if ( ege_select_one_action_get_active( act ) != 0 ) {
3755 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3756 } else {
3757 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3758 }
3759 }
3761 // quit if run by the attr_changed listener
3762 if (g_object_get_data( tbl, "freeze" )) {
3763 return;
3764 }
3766 // in turn, prevent listener from responding
3767 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3769 bool modmade = false;
3771 if ( ege_select_one_action_get_active(act) != 0 ) {
3772 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3773 items != NULL;
3774 items = items->next)
3775 {
3776 if (SP_IS_ARC((SPItem *) items->data)) {
3777 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3778 repr->setAttribute("sodipodi:open", "true");
3779 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3780 modmade = true;
3781 }
3782 }
3783 } else {
3784 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3785 items != NULL;
3786 items = items->next)
3787 {
3788 if (SP_IS_ARC((SPItem *) items->data)) {
3789 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3790 repr->setAttribute("sodipodi:open", NULL);
3791 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3792 modmade = true;
3793 }
3794 }
3795 }
3797 if (modmade) {
3798 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3799 _("Arc: Change open/closed"));
3800 }
3802 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3803 }
3805 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3806 {
3807 GtkAdjustment *adj;
3808 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3809 gtk_adjustment_set_value(adj, 0.0);
3810 gtk_adjustment_value_changed(adj);
3812 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3813 gtk_adjustment_set_value(adj, 0.0);
3814 gtk_adjustment_value_changed(adj);
3816 spinbutton_defocus( GTK_OBJECT(obj) );
3817 }
3819 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3820 gchar const */*old_value*/, gchar const */*new_value*/,
3821 bool /*is_interactive*/, gpointer data)
3822 {
3823 GObject *tbl = G_OBJECT(data);
3825 // quit if run by the _changed callbacks
3826 if (g_object_get_data( tbl, "freeze" )) {
3827 return;
3828 }
3830 // in turn, prevent callbacks from responding
3831 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3833 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3834 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3836 GtkAdjustment *adj1,*adj2;
3837 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3838 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3839 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3840 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3842 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3844 char const *openstr = NULL;
3845 openstr = repr->attribute("sodipodi:open");
3846 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3848 if (openstr) {
3849 ege_select_one_action_set_active( ocb, 1 );
3850 } else {
3851 ege_select_one_action_set_active( ocb, 0 );
3852 }
3854 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3855 }
3857 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3858 NULL, /* child_added */
3859 NULL, /* child_removed */
3860 arc_tb_event_attr_changed,
3861 NULL, /* content_changed */
3862 NULL /* order_changed */
3863 };
3866 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3867 {
3868 int n_selected = 0;
3869 Inkscape::XML::Node *repr = NULL;
3871 purge_repr_listener( tbl, tbl );
3873 for (GSList const *items = selection->itemList();
3874 items != NULL;
3875 items = items->next)
3876 {
3877 if (SP_IS_ARC((SPItem *) items->data)) {
3878 n_selected++;
3879 repr = SP_OBJECT_REPR((SPItem *) items->data);
3880 }
3881 }
3883 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3885 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3886 if (n_selected == 0) {
3887 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3888 } else if (n_selected == 1) {
3889 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3890 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3892 if (repr) {
3893 g_object_set_data( tbl, "repr", repr );
3894 Inkscape::GC::anchor(repr);
3895 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3896 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3897 }
3898 } else {
3899 // FIXME: implement averaging of all parameters for multiple selected
3900 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3901 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3902 sp_arctb_sensitivize( tbl, 1, 0 );
3903 }
3904 }
3907 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3908 {
3909 EgeAdjustmentAction* eact = 0;
3912 {
3913 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3914 ege_output_action_set_use_markup( act, TRUE );
3915 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3916 g_object_set_data( holder, "mode_action", act );
3917 }
3919 /* Start */
3920 {
3921 eact = create_adjustment_action( "ArcStartAction",
3922 _("Start"), _("Start:"),
3923 _("The angle (in degrees) from the horizontal to the arc's start point"),
3924 "tools.shapes.arc", "start", 0.0,
3925 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3926 -360.0, 360.0, 1.0, 10.0,
3927 0, 0, 0,
3928 sp_arctb_start_value_changed);
3929 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3930 }
3932 /* End */
3933 {
3934 eact = create_adjustment_action( "ArcEndAction",
3935 _("End"), _("End:"),
3936 _("The angle (in degrees) from the horizontal to the arc's end point"),
3937 "tools.shapes.arc", "end", 0.0,
3938 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3939 -360.0, 360.0, 1.0, 10.0,
3940 0, 0, 0,
3941 sp_arctb_end_value_changed);
3942 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3943 }
3945 /* Segments / Pie checkbox */
3946 {
3947 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3949 GtkTreeIter iter;
3950 gtk_list_store_append( model, &iter );
3951 gtk_list_store_set( model, &iter,
3952 0, _("Closed arc"),
3953 1, _("Switch to segment (closed shape with two radii)"),
3954 2, "circle_closed_arc",
3955 -1 );
3957 gtk_list_store_append( model, &iter );
3958 gtk_list_store_set( model, &iter,
3959 0, _("Open Arc"),
3960 1, _("Switch to arc (unclosed shape)"),
3961 2, "circle_open_arc",
3962 -1 );
3964 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3965 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3966 g_object_set_data( holder, "open_action", act );
3968 ege_select_one_action_set_appearance( act, "full" );
3969 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3970 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3971 ege_select_one_action_set_icon_column( act, 2 );
3972 ege_select_one_action_set_tooltip_column( act, 1 );
3974 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3975 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3976 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3977 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3978 }
3980 /* Make Whole */
3981 {
3982 InkAction* inky = ink_action_new( "ArcResetAction",
3983 _("Make whole"),
3984 _("Make the shape a whole ellipse, not arc or segment"),
3985 "reset_circle",
3986 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3987 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3988 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3989 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3990 g_object_set_data( holder, "make_whole", inky );
3991 }
3993 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3994 // sensitivize make whole and open checkbox
3995 {
3996 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3997 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3998 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3999 }
4002 sigc::connection *connection = new sigc::connection(
4003 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4004 );
4005 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4006 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4007 }
4012 // toggle button callbacks and updaters
4014 //########################
4015 //## Dropper ##
4016 //########################
4018 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4019 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4020 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4021 if ( set_action ) {
4022 if ( gtk_toggle_action_get_active( act ) ) {
4023 gtk_action_set_sensitive( set_action, TRUE );
4024 } else {
4025 gtk_action_set_sensitive( set_action, FALSE );
4026 }
4027 }
4029 spinbutton_defocus(GTK_OBJECT(tbl));
4030 }
4032 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4033 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4034 spinbutton_defocus(GTK_OBJECT(tbl));
4035 }
4038 /**
4039 * Dropper auxiliary toolbar construction and setup.
4040 *
4041 * TODO: Would like to add swatch of current color.
4042 * TODO: Add queue of last 5 or so colors selected with new swatches so that
4043 * can drag and drop places. Will provide a nice mixing palette.
4044 */
4045 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4046 {
4047 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4049 {
4050 EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4051 ege_output_action_set_use_markup( act, TRUE );
4052 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4053 }
4055 {
4056 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4057 _("Pick opacity"),
4058 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4059 NULL,
4060 Inkscape::ICON_SIZE_DECORATION );
4061 g_object_set( act, "short_label", _("Pick"), NULL );
4062 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4063 g_object_set_data( holder, "pick_action", act );
4064 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4065 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4066 }
4068 {
4069 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4070 _("Assign opacity"),
4071 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4072 NULL,
4073 Inkscape::ICON_SIZE_DECORATION );
4074 g_object_set( act, "short_label", _("Assign"), NULL );
4075 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4076 g_object_set_data( holder, "set_action", act );
4077 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4078 // make sure it's disabled if we're not picking alpha
4079 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4080 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4081 }
4082 }
4085 //########################
4086 //## Text Toolbox ##
4087 //########################
4088 /*
4089 static void
4090 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
4091 {
4092 //Call back for letter sizing spinbutton
4093 }
4095 static void
4096 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
4097 {
4098 //Call back for line height spinbutton
4099 }
4101 static void
4102 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4103 {
4104 //Call back for horizontal kerning spinbutton
4105 }
4107 static void
4108 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
4109 {
4110 //Call back for vertical kerning spinbutton
4111 }
4113 static void
4114 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
4115 {
4116 //Call back for letter rotation spinbutton
4117 }*/
4119 namespace {
4121 bool popdown_visible = false;
4122 bool popdown_hasfocus = false;
4124 void
4125 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
4126 {
4127 SPStyle *query =
4128 sp_style_new (SP_ACTIVE_DOCUMENT);
4130 int result_fontspec =
4131 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4133 int result_family =
4134 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4136 int result_style =
4137 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4139 int result_numbers =
4140 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4142 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4144 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4145 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4146 {
4147 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4149 if (repr)
4150 {
4151 sp_style_read_from_repr (query, repr);
4152 }
4153 else
4154 {
4155 return;
4156 }
4157 }
4159 if (query->text)
4160 {
4161 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4162 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4163 gtk_entry_set_text (GTK_ENTRY (entry), "");
4165 } else if (query->text->font_specification.value || query->text->font_family.value) {
4167 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4169 // Get the font that corresponds
4170 Glib::ustring familyName;
4172 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4173 if (font) {
4174 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4175 font->Unref();
4176 font = NULL;
4177 }
4179 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4181 Gtk::TreePath path;
4182 try {
4183 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4184 } catch (...) {
4185 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4186 return;
4187 }
4189 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4190 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4192 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4194 gtk_tree_selection_select_path (tselection, path.gobj());
4195 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4197 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4198 }
4200 //Size
4201 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4202 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4203 g_object_set_data (tbl, "size-block", gpointer(1));
4204 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4205 g_object_set_data (tbl, "size-block", gpointer(0));
4206 free (str);
4208 //Anchor
4209 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4210 {
4211 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4212 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4213 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4214 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4215 }
4216 else
4217 {
4218 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4219 {
4220 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4221 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4222 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4223 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4224 }
4225 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4226 {
4227 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4228 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4229 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4230 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4231 }
4232 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4233 {
4234 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4235 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4236 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4237 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4238 }
4239 }
4241 //Style
4242 {
4243 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4245 gboolean active = gtk_toggle_button_get_active (button);
4246 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4248 if (active != check)
4249 {
4250 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4251 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4252 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4253 }
4254 }
4256 {
4257 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4259 gboolean active = gtk_toggle_button_get_active (button);
4260 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4262 if (active != check)
4263 {
4264 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4265 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4266 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4267 }
4268 }
4270 //Orientation
4271 //locking both buttons, changing one affect all group (both)
4272 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4273 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4275 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4276 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4278 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4279 {
4280 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4281 }
4282 else
4283 {
4284 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4285 }
4286 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4287 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4288 }
4290 sp_style_unref(query);
4291 }
4293 void
4294 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4295 {
4296 sp_text_toolbox_selection_changed (selection, tbl);
4297 }
4299 void
4300 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4301 {
4302 sp_text_toolbox_selection_changed (NULL, tbl);
4303 }
4305 void
4306 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
4307 GObject *tbl)
4308 {
4309 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4310 GtkTreeModel *model = 0;
4311 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4312 GtkTreeIter iter;
4313 char *family = 0;
4315 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4316 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4318 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4319 return;
4320 }
4322 gtk_tree_model_get (model, &iter, 0, &family, -1);
4324 if (g_object_get_data (G_OBJECT (selection), "block"))
4325 {
4326 gtk_entry_set_text (GTK_ENTRY (entry), family);
4327 return;
4328 }
4330 gtk_entry_set_text (GTK_ENTRY (entry), family);
4332 SPStyle *query =
4333 sp_style_new (SP_ACTIVE_DOCUMENT);
4335 int result_fontspec =
4336 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4338 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4340 SPCSSAttr *css = sp_repr_css_attr_new ();
4343 // First try to get the font spec from the stored value
4344 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4346 if (fontSpec.empty()) {
4347 // Construct a new font specification if it does not yet exist
4348 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4349 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4350 fontFromStyle->Unref();
4351 }
4353 if (!fontSpec.empty()) {
4354 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4355 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4356 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4357 if (font) {
4358 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4360 // Set all the these just in case they were altered when finding the best
4361 // match for the new family and old style...
4363 gchar c[256];
4365 font->Family(c, 256);
4366 sp_repr_css_set_property (css, "font-family", c);
4368 font->Attribute( "weight", c, 256);
4369 sp_repr_css_set_property (css, "font-weight", c);
4371 font->Attribute("style", c, 256);
4372 sp_repr_css_set_property (css, "font-style", c);
4374 font->Attribute("stretch", c, 256);
4375 sp_repr_css_set_property (css, "font-stretch", c);
4377 font->Attribute("variant", c, 256);
4378 sp_repr_css_set_property (css, "font-variant", c);
4380 font->Unref();
4381 }
4382 }
4383 }
4385 // If querying returned nothing, set the default style of the tool (for new texts)
4386 if (result_fontspec == QUERY_STYLE_NOTHING)
4387 {
4388 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4389 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4390 }
4391 else
4392 {
4393 sp_desktop_set_style (desktop, css, true, true);
4394 }
4396 sp_style_unref(query);
4398 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4399 _("Text: Change font family"));
4400 sp_repr_css_attr_unref (css);
4401 free (family);
4402 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4404 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4405 }
4407 void
4408 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
4409 GObject *tbl)
4410 {
4411 const char *family = gtk_entry_get_text (entry);
4413 try {
4414 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4415 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4416 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4417 gtk_tree_selection_select_path (selection, path.gobj());
4418 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4419 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4420 } catch (...) {
4421 if (family && strlen (family))
4422 {
4423 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4424 }
4425 }
4426 }
4428 void
4429 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
4430 gpointer data)
4431 {
4432 if (g_object_get_data (G_OBJECT (button), "block")) return;
4433 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4434 int prop = GPOINTER_TO_INT(data);
4436 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4437 SPCSSAttr *css = sp_repr_css_attr_new ();
4439 switch (prop)
4440 {
4441 case 0:
4442 {
4443 sp_repr_css_set_property (css, "text-anchor", "start");
4444 sp_repr_css_set_property (css, "text-align", "start");
4445 break;
4446 }
4447 case 1:
4448 {
4449 sp_repr_css_set_property (css, "text-anchor", "middle");
4450 sp_repr_css_set_property (css, "text-align", "center");
4451 break;
4452 }
4454 case 2:
4455 {
4456 sp_repr_css_set_property (css, "text-anchor", "end");
4457 sp_repr_css_set_property (css, "text-align", "end");
4458 break;
4459 }
4461 case 3:
4462 {
4463 sp_repr_css_set_property (css, "text-anchor", "start");
4464 sp_repr_css_set_property (css, "text-align", "justify");
4465 break;
4466 }
4467 }
4469 SPStyle *query =
4470 sp_style_new (SP_ACTIVE_DOCUMENT);
4471 int result_numbers =
4472 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4474 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4475 if (result_numbers == QUERY_STYLE_NOTHING)
4476 {
4477 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4478 }
4480 sp_style_unref(query);
4482 sp_desktop_set_style (desktop, css, true, true);
4483 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4484 _("Text: Change alignment"));
4485 sp_repr_css_attr_unref (css);
4487 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4488 }
4490 void
4491 sp_text_toolbox_style_toggled (GtkToggleButton *button,
4492 gpointer data)
4493 {
4494 if (g_object_get_data (G_OBJECT (button), "block")) return;
4496 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4497 SPCSSAttr *css = sp_repr_css_attr_new ();
4498 int prop = GPOINTER_TO_INT(data);
4499 bool active = gtk_toggle_button_get_active (button);
4501 SPStyle *query =
4502 sp_style_new (SP_ACTIVE_DOCUMENT);
4504 int result_fontspec =
4505 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4507 int result_family =
4508 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4510 int result_style =
4511 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4513 int result_numbers =
4514 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4516 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4517 Glib::ustring newFontSpec = "";
4519 if (fontSpec.empty()) {
4520 // Construct a new font specification if it does not yet exist
4521 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4522 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4523 fontFromStyle->Unref();
4524 }
4526 switch (prop)
4527 {
4528 case 0:
4529 {
4530 if (!fontSpec.empty()) {
4531 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4532 }
4533 if (fontSpec != newFontSpec) {
4534 // Don't even set the bold if the font didn't exist on the system
4535 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4536 }
4537 break;
4538 }
4540 case 1:
4541 {
4542 if (!fontSpec.empty()) {
4543 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4544 }
4545 if (fontSpec != newFontSpec) {
4546 // Don't even set the italic if the font didn't exist on the system
4547 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4548 }
4549 break;
4550 }
4551 }
4553 if (!newFontSpec.empty()) {
4554 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4555 }
4557 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4558 if (result_fontspec == QUERY_STYLE_NOTHING)
4559 {
4560 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4561 }
4563 sp_style_unref(query);
4565 sp_desktop_set_style (desktop, css, true, true);
4566 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4567 _("Text: Change font style"));
4568 sp_repr_css_attr_unref (css);
4570 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4571 }
4573 void
4574 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
4575 gpointer data)
4576 {
4577 if (g_object_get_data (G_OBJECT (button), "block")) {
4578 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4579 return;
4580 }
4582 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4583 SPCSSAttr *css = sp_repr_css_attr_new ();
4584 int prop = GPOINTER_TO_INT(data);
4586 switch (prop)
4587 {
4588 case 0:
4589 {
4590 sp_repr_css_set_property (css, "writing-mode", "lr");
4591 break;
4592 }
4594 case 1:
4595 {
4596 sp_repr_css_set_property (css, "writing-mode", "tb");
4597 break;
4598 }
4599 }
4601 SPStyle *query =
4602 sp_style_new (SP_ACTIVE_DOCUMENT);
4603 int result_numbers =
4604 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4606 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4607 if (result_numbers == QUERY_STYLE_NOTHING)
4608 {
4609 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4610 }
4612 sp_desktop_set_style (desktop, css, true, true);
4613 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4614 _("Text: Change orientation"));
4615 sp_repr_css_attr_unref (css);
4617 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4618 }
4620 gboolean
4621 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4622 {
4623 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4624 if (!desktop) return FALSE;
4626 switch (get_group0_keyval (event)) {
4627 case GDK_Escape: // defocus
4628 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4629 sp_text_toolbox_selection_changed (NULL, tbl); // update
4630 return TRUE; // I consumed the event
4631 break;
4632 }
4633 return FALSE;
4634 }
4636 gboolean
4637 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4638 {
4639 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4640 if (!desktop) return FALSE;
4642 switch (get_group0_keyval (event)) {
4643 case GDK_KP_Enter:
4644 case GDK_Return:
4645 case GDK_Escape: // defocus
4646 gtk_widget_hide (w);
4647 popdown_visible = false;
4648 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4649 return TRUE; // I consumed the event
4650 break;
4651 case GDK_w:
4652 case GDK_W:
4653 if (event->state & GDK_CONTROL_MASK) {
4654 gtk_widget_hide (w);
4655 popdown_visible = false;
4656 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4657 return TRUE; // I consumed the event
4658 }
4659 break;
4660 }
4661 return FALSE;
4662 }
4665 void
4666 sp_text_toolbox_size_changed (GtkComboBox *cbox,
4667 GObject *tbl)
4668 {
4669 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4671 if (g_object_get_data (tbl, "size-block")) return;
4673 // If this is not from selecting a size in the list (in which case get_active will give the
4674 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4675 // process this event. This fixes GTK's stupid insistence on sending an activate change every
4676 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4677 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4678 return;
4680 gchar *endptr;
4681 gdouble value = -1;
4682 char *text = gtk_combo_box_get_active_text (cbox);
4683 if (text) {
4684 value = g_strtod (text, &endptr);
4685 if (endptr == text) // conversion failed, non-numeric input
4686 value = -1;
4687 free (text);
4688 }
4689 if (value <= 0) {
4690 return; // could not parse value
4691 }
4693 SPCSSAttr *css = sp_repr_css_attr_new ();
4694 Inkscape::CSSOStringStream osfs;
4695 osfs << value;
4696 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4698 SPStyle *query =
4699 sp_style_new (SP_ACTIVE_DOCUMENT);
4700 int result_numbers =
4701 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4703 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4704 if (result_numbers == QUERY_STYLE_NOTHING)
4705 {
4706 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4707 }
4709 sp_style_unref(query);
4711 sp_desktop_set_style (desktop, css, true, true);
4712 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4713 _("Text: Change font size"));
4714 sp_repr_css_attr_unref (css);
4716 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4717 }
4719 gboolean
4720 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
4721 {
4722 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4723 if (!desktop) return FALSE;
4725 if (!g_object_get_data (tbl, "esc-pressed")) {
4726 g_object_set_data (tbl, "enter-pressed", gpointer(1));
4727 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4728 sp_text_toolbox_size_changed (cbox, tbl);
4729 g_object_set_data (tbl, "enter-pressed", gpointer(0));
4730 }
4731 return FALSE; // I consumed the event
4732 }
4735 gboolean
4736 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4737 {
4738 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4739 if (!desktop) return FALSE;
4741 switch (get_group0_keyval (event)) {
4742 case GDK_Escape: // defocus
4743 g_object_set_data (tbl, "esc-pressed", gpointer(1));
4744 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4745 g_object_set_data (tbl, "esc-pressed", gpointer(0));
4746 return TRUE; // I consumed the event
4747 break;
4748 case GDK_Return: // defocus
4749 case GDK_KP_Enter:
4750 g_object_set_data (tbl, "enter-pressed", gpointer(1));
4751 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4752 sp_text_toolbox_size_changed (cbox, tbl);
4753 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4754 g_object_set_data (tbl, "enter-pressed", gpointer(0));
4755 return TRUE; // I consumed the event
4756 break;
4757 }
4758 return FALSE;
4759 }
4761 void
4762 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
4763 GObject *tbl)
4764 {
4765 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4766 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4767 int x, y;
4769 if (!popdown_visible)
4770 {
4771 gdk_window_get_origin (widget->window, &x, &y);
4772 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4773 gtk_widget_show_all (popdown);
4774 //sp_transientize (popdown);
4776 gdk_pointer_grab (widget->window, TRUE,
4777 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4778 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4779 GDK_POINTER_MOTION_MASK),
4780 NULL, NULL, GDK_CURRENT_TIME);
4782 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4784 popdown_visible = true;
4785 }
4786 else
4787 {
4788 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4789 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4790 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4791 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4792 gtk_widget_hide (popdown);
4793 popdown_visible = false;
4794 }
4795 }
4797 gboolean
4798 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
4799 GdkEventFocus */*event*/,
4800 GObject */*tbl*/)
4801 {
4802 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4803 return FALSE;
4804 }
4806 gboolean
4807 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
4808 GdkEventFocus */*event*/,
4809 GObject */*tbl*/)
4810 {
4811 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4813 if (popdown_hasfocus) {
4814 gtk_widget_hide (popdown);
4815 popdown_hasfocus = false;
4816 popdown_visible = false;
4817 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4818 return TRUE;
4819 }
4820 return FALSE;
4821 }
4823 gboolean
4824 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
4825 GdkEventFocus */*event*/,
4826 GObject */*tbl*/)
4827 {
4828 popdown_hasfocus = true;
4829 return TRUE;
4830 }
4833 void
4834 cell_data_func (GtkTreeViewColumn */*column*/,
4835 GtkCellRenderer *cell,
4836 GtkTreeModel *tree_model,
4837 GtkTreeIter *iter,
4838 gpointer /*data*/)
4839 {
4840 char *family,
4841 *family_escaped,
4842 *sample_escaped;
4844 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4846 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4848 family_escaped = g_markup_escape_text (family, -1);
4849 sample_escaped = g_markup_escape_text (sample, -1);
4851 std::stringstream markup;
4852 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4853 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4855 free (family);
4856 free (family_escaped);
4857 free (sample_escaped);
4858 }
4860 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4861 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4862 if (completion) {
4863 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4864 g_object_unref (completion);
4865 }
4866 }
4868 GtkWidget*
4869 sp_text_toolbox_new (SPDesktop *desktop)
4870 {
4871 GtkWidget *tbl = gtk_hbox_new (FALSE, 0);
4873 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4874 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4876 GtkTooltips *tt = gtk_tooltips_new();
4877 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4879 ////////////Family
4880 //Window
4881 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4882 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4884 //Entry
4885 GtkWidget *entry = gtk_entry_new ();
4886 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4887 GtkEntryCompletion *completion = gtk_entry_completion_new ();
4888 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4889 gtk_entry_completion_set_text_column (completion, 0);
4890 gtk_entry_completion_set_minimum_key_length (completion, 1);
4891 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4892 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4893 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4894 aux_toolbox_space (tbl, 1);
4895 gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4896 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4898 //Button
4899 GtkWidget *button = gtk_button_new ();
4900 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4901 gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4903 //Popdown
4904 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
4905 GtkWidget *treeview = gtk_tree_view_new ();
4907 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
4908 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
4909 gtk_tree_view_column_pack_start (column, cell, FALSE);
4910 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4911 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4912 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4914 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4915 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4916 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4918 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4920 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4921 gtk_container_add (GTK_CONTAINER (sw), treeview);
4923 gtk_container_add (GTK_CONTAINER (window), sw);
4924 gtk_widget_set_size_request (window, 300, 450);
4926 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4927 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4928 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4930 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4932 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4933 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
4934 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4936 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4937 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4939 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4940 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4941 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4942 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4943 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4945 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4946 aux_toolbox_space (tbl, 1);
4947 GtkWidget *box = gtk_event_box_new ();
4948 gtk_container_add (GTK_CONTAINER (box), image);
4949 gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4950 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4951 GtkTooltips *tooltips = gtk_tooltips_new ();
4952 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4953 gtk_widget_hide (GTK_WIDGET (box));
4954 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4956 ////////////Size
4957 const char *sizes[] = {
4958 "4", "6", "8", "9", "10", "11", "12", "13", "14",
4959 "16", "18", "20", "22", "24", "28",
4960 "32", "36", "40", "48", "56", "64", "72", "144"
4961 };
4963 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4964 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4965 gtk_widget_set_size_request (cbox, 80, -1);
4966 aux_toolbox_space (tbl, 1);
4967 gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4968 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4969 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4970 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
4971 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
4973 //spacer
4974 aux_toolbox_space (tbl, 4);
4975 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4977 ////////////Text anchor
4978 GtkWidget *group = gtk_radio_button_new (NULL);
4979 GtkWidget *row = gtk_hbox_new (FALSE, 4);
4980 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4982 // left
4983 GtkWidget *rbutton = group;
4984 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4985 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4986 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4988 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4989 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
4990 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4991 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4993 // center
4994 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4995 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4996 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4997 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4999 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5000 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
5001 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
5002 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
5004 // right
5005 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5006 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5007 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
5008 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5010 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5011 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
5012 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
5013 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
5015 // fill
5016 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5017 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5018 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
5019 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5021 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5022 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
5023 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
5024 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
5026 aux_toolbox_space (tbl, 1);
5027 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5029 //spacer
5030 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5032 ////////////Text style
5033 row = gtk_hbox_new (FALSE, 4);
5035 // bold
5036 rbutton = gtk_toggle_button_new ();
5037 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5038 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
5039 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5040 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
5042 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5043 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
5044 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
5046 // italic
5047 rbutton = gtk_toggle_button_new ();
5048 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5049 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
5050 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5051 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
5053 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5054 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
5055 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
5057 aux_toolbox_space (tbl, 1);
5058 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5060 //spacer
5061 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
5063 ////////////Text orientation
5064 group = gtk_radio_button_new (NULL);
5065 row = gtk_hbox_new (FALSE, 4);
5066 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
5068 // horizontal
5069 rbutton = group;
5070 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5071 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
5072 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5073 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
5075 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5076 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
5077 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
5079 // vertical
5080 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
5081 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
5082 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
5083 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
5084 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
5086 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
5087 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
5088 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
5089 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
5092 //watch selection
5093 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
5095 sigc::connection *c_selection_changed =
5096 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5097 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
5098 pool->add_connection ("selection-changed", c_selection_changed);
5100 sigc::connection *c_selection_modified =
5101 new sigc::connection (sp_desktop_selection (desktop)->connectModified
5102 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
5103 pool->add_connection ("selection-modified", c_selection_modified);
5105 sigc::connection *c_subselection_changed =
5106 new sigc::connection (desktop->connectToolSubselectionChanged
5107 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
5108 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
5110 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
5113 gtk_widget_show_all (tbl);
5114 return tbl;
5116 } // end of sp_text_toolbox_new()
5118 }//<unnamed> namespace
5121 //#########################
5122 //## Connector ##
5123 //#########################
5125 static void sp_connector_path_set_avoid(void)
5126 {
5127 cc_selection_set_avoid(true);
5128 }
5131 static void sp_connector_path_set_ignore(void)
5132 {
5133 cc_selection_set_avoid(false);
5134 }
5138 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
5139 {
5140 // quit if run by the _changed callbacks
5141 if (g_object_get_data( tbl, "freeze" )) {
5142 return;
5143 }
5145 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5146 SPDocument *doc = sp_desktop_document(desktop);
5148 if (!sp_document_get_undo_sensitive(doc))
5149 {
5150 return;
5151 }
5153 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5155 if ( repr->attribute("inkscape:connector-spacing") ) {
5156 gdouble priorValue = gtk_adjustment_get_value(adj);
5157 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5158 if ( priorValue == gtk_adjustment_get_value(adj) ) {
5159 return;
5160 }
5161 } else if ( adj->value == defaultConnSpacing ) {
5162 return;
5163 }
5165 // in turn, prevent callbacks from responding
5166 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5168 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5169 SP_OBJECT(desktop->namedview)->updateRepr();
5171 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5172 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5173 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5174 NR::Matrix m = NR::identity();
5175 avoid_item_move(&m, item);
5176 }
5178 if (items) {
5179 g_slist_free(items);
5180 }
5182 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5183 _("Change connector spacing"));
5185 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5187 spinbutton_defocus(GTK_OBJECT(tbl));
5188 }
5190 static void sp_connector_graph_layout(void)
5191 {
5192 if (!SP_ACTIVE_DESKTOP) return;
5194 // hack for clones, see comment in align-and-distribute.cpp
5195 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5196 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5198 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5200 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5202 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5203 }
5205 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5206 {
5207 if ( gtk_toggle_action_get_active( act ) ) {
5208 prefs_set_string_attribute("tools.connector", "directedlayout",
5209 "true");
5210 } else {
5211 prefs_set_string_attribute("tools.connector", "directedlayout",
5212 "false");
5213 }
5214 }
5216 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5217 {
5218 if ( gtk_toggle_action_get_active( act ) ) {
5219 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5220 "true");
5221 } else {
5222 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5223 "false");
5224 }
5225 }
5228 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5229 {
5230 prefs_set_double_attribute("tools.connector", "length", adj->value);
5231 spinbutton_defocus(GTK_OBJECT(tbl));
5232 }
5234 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5235 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5236 bool /*is_interactive*/, gpointer data)
5237 {
5238 GtkWidget *tbl = GTK_WIDGET(data);
5240 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5241 return;
5242 }
5243 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5244 return;
5245 }
5247 GtkAdjustment *adj = (GtkAdjustment*)
5248 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5249 gdouble spacing = defaultConnSpacing;
5250 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5252 gtk_adjustment_set_value(adj, spacing);
5253 }
5256 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5257 NULL, /* child_added */
5258 NULL, /* child_removed */
5259 connector_tb_event_attr_changed,
5260 NULL, /* content_changed */
5261 NULL /* order_changed */
5262 };
5265 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5266 {
5267 {
5268 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5269 _("Avoid"),
5270 _("Make connectors avoid selected objects"),
5271 "connector_avoid",
5272 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5273 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5274 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5275 }
5277 {
5278 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5279 _("Ignore"),
5280 _("Make connectors ignore selected objects"),
5281 "connector_ignore",
5282 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5283 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5284 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5285 }
5287 EgeAdjustmentAction* eact = 0;
5289 // Spacing spinbox
5290 eact = create_adjustment_action( "ConnectorSpacingAction",
5291 _("Connector Spacing"), _("Spacing:"),
5292 _("The amount of space left around objects by auto-routing connectors"),
5293 "tools.connector", "spacing", defaultConnSpacing,
5294 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5295 0, 100, 1.0, 10.0,
5296 0, 0, 0,
5297 connector_spacing_changed, 1, 0 );
5298 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5300 // Graph (connector network) layout
5301 {
5302 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5303 _("Graph"),
5304 _("Nicely arrange selected connector network"),
5305 "graph_layout",
5306 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5307 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5308 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5309 }
5311 // Default connector length spinbox
5312 eact = create_adjustment_action( "ConnectorLengthAction",
5313 _("Connector Length"), _("Length:"),
5314 _("Ideal length for connectors when layout is applied"),
5315 "tools.connector", "length", 100,
5316 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5317 10, 1000, 10.0, 100.0,
5318 0, 0, 0,
5319 connector_length_changed, 1, 0 );
5320 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5323 // Directed edges toggle button
5324 {
5325 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5326 _("Downwards"),
5327 _("Make connectors with end-markers (arrows) point downwards"),
5328 "directed_graph",
5329 Inkscape::ICON_SIZE_DECORATION );
5330 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5332 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5333 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5334 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5336 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5337 }
5339 // Avoid overlaps toggle button
5340 {
5341 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5342 _("Remove overlaps"),
5343 _("Do not allow overlapping shapes"),
5344 "remove_overlaps",
5345 Inkscape::ICON_SIZE_DECORATION );
5346 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5348 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5349 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5350 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5352 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5353 }
5355 // Code to watch for changes to the connector-spacing attribute in
5356 // the XML.
5357 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5358 g_assert(repr != NULL);
5360 purge_repr_listener( holder, holder );
5362 if (repr) {
5363 g_object_set_data( holder, "repr", repr );
5364 Inkscape::GC::anchor(repr);
5365 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5366 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5367 }
5368 } // end of sp_connector_toolbox_prep()
5371 //#########################
5372 //## Paintbucket ##
5373 //#########################
5375 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5376 {
5377 gint channels = ege_select_one_action_get_active( act );
5378 flood_channels_set_channels( channels );
5379 }
5381 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5382 {
5383 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5384 }
5386 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5387 {
5388 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5389 }
5391 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5392 {
5393 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5394 SPUnit const *unit = tracker->getActiveUnit();
5396 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5398 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5399 }
5401 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5402 {
5403 // FIXME: make defaults settable via Inkscape Options
5404 struct KeyValue {
5405 char const *key;
5406 double value;
5407 } const key_values[] = {
5408 {"threshold", 15},
5409 {"offset", 0.0}
5410 };
5412 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5413 KeyValue const &kv = key_values[i];
5414 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5415 if ( adj ) {
5416 gtk_adjustment_set_value(adj, kv.value);
5417 }
5418 }
5420 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5421 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5422 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5423 ege_select_one_action_set_active( autogap_action, 0 );
5424 }
5426 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5427 {
5428 EgeAdjustmentAction* eact = 0;
5430 {
5431 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5433 GList* items = 0;
5434 gint count = 0;
5435 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5436 {
5437 GtkTreeIter iter;
5438 gtk_list_store_append( model, &iter );
5439 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5440 count++;
5441 }
5442 g_list_free( items );
5443 items = 0;
5444 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5445 g_object_set( act1, "short_label", _("Fill by:"), NULL );
5446 ege_select_one_action_set_appearance( act1, "compact" );
5447 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5448 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5449 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5450 g_object_set_data( holder, "channels_action", act1 );
5451 }
5453 // Spacing spinbox
5454 {
5455 eact = create_adjustment_action(
5456 "ThresholdAction",
5457 _("Fill Threshold"), _("Threshold:"),
5458 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5459 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5460 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5461 0, 0, 0,
5462 paintbucket_threshold_changed, 1, 0 );
5464 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5465 }
5467 // Create the units menu.
5468 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5469 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5470 if (stored_unit)
5471 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5472 g_object_set_data( holder, "tracker", tracker );
5473 {
5474 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5475 gtk_action_group_add_action( mainActions, act );
5476 }
5478 // Offset spinbox
5479 {
5480 eact = create_adjustment_action(
5481 "OffsetAction",
5482 _("Grow/shrink by"), _("Grow/shrink by:"),
5483 _("The amount to grow (positive) or shrink (negative) the created fill path"),
5484 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5485 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5486 0, 0, 0,
5487 paintbucket_offset_changed, 1, 2);
5488 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5490 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5491 }
5493 /* Auto Gap */
5494 {
5495 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5497 GList* items = 0;
5498 gint count = 0;
5499 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5500 {
5501 GtkTreeIter iter;
5502 gtk_list_store_append( model, &iter );
5503 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5504 count++;
5505 }
5506 g_list_free( items );
5507 items = 0;
5508 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5509 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5510 ege_select_one_action_set_appearance( act2, "compact" );
5511 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5512 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5513 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5514 g_object_set_data( holder, "autogap_action", act2 );
5515 }
5517 /* Reset */
5518 {
5519 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5520 _("Defaults"),
5521 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5522 GTK_STOCK_CLEAR );
5523 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5524 gtk_action_group_add_action( mainActions, act );
5525 gtk_action_set_sensitive( act, TRUE );
5526 }
5528 }
5530 /*
5531 Local Variables:
5532 mode:c++
5533 c-file-style:"stroustrup"
5534 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5535 indent-tabs-mode:nil
5536 fill-column:99
5537 End:
5538 */
5539 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :