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 <gtkmm.h>
31 #include <gtk/gtk.h>
32 #include <iostream>
33 #include <sstream>
35 #include "widgets/button.h"
36 #include "widgets/widget-sizes.h"
37 #include "widgets/spw-utilities.h"
38 #include "widgets/spinbutton-events.h"
39 #include "dialogs/text-edit.h"
41 #include "ui/widget/style-swatch.h"
43 #include "prefs-utils.h"
44 #include "verbs.h"
45 #include "sp-namedview.h"
46 #include "desktop.h"
47 #include "desktop-handles.h"
48 #include "xml/repr.h"
49 #include "xml/node-event-vector.h"
50 #include <glibmm/i18n.h>
51 #include "helper/unit-menu.h"
52 #include "helper/units.h"
54 #include "inkscape.h"
55 #include "conn-avoid-ref.h"
58 #include "select-toolbar.h"
59 #include "gradient-toolbar.h"
61 #include "connector-context.h"
62 #include "node-context.h"
63 #include "shape-editor.h"
64 #include "tweak-context.h"
65 #include "sp-rect.h"
66 #include "box3d.h"
67 #include "box3d-context.h"
68 #include "sp-star.h"
69 #include "sp-spiral.h"
70 #include "sp-ellipse.h"
71 #include "sp-text.h"
72 #include "sp-flowtext.h"
73 #include "style.h"
74 #include "selection.h"
75 #include "selection-chemistry.h"
76 #include "document-private.h"
77 #include "desktop-style.h"
78 #include "../libnrtype/font-lister.h"
79 #include "../libnrtype/font-instance.h"
80 #include "../connection-pool.h"
81 #include "../prefs-utils.h"
82 #include "../inkscape-stock.h"
83 #include "icon.h"
84 #include "graphlayout/graphlayout.h"
86 #include "mod360.h"
88 #include "toolbox.h"
90 #include "flood-context.h"
92 #include "ink-action.h"
93 #include "ege-adjustment-action.h"
94 #include "ege-output-action.h"
95 #include "ege-select-one-action.h"
96 #include "helper/unit-tracker.h"
98 using Inkscape::UnitTracker;
100 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
101 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
103 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
104 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
105 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
106 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
107 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
108 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
109 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
116 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
122 static struct {
123 gchar const *type_name;
124 gchar const *data_name;
125 sp_verb_t verb;
126 sp_verb_t doubleclick_verb;
127 } const tools[] = {
128 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
129 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
130 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
131 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
132 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
133 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
134 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
135 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
136 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
137 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
138 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
139 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
140 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
141 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
142 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
143 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
144 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
145 { NULL, NULL, 0, 0 }
146 };
148 static struct {
149 gchar const *type_name;
150 gchar const *data_name;
151 GtkWidget *(*create_func)(SPDesktop *desktop);
152 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
153 gchar const *ui_name;
154 gint swatch_verb_id;
155 gchar const *swatch_tool;
156 gchar const *swatch_tip;
157 } const aux_toolboxes[] = {
158 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
159 SP_VERB_INVALID, 0, 0},
160 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
161 SP_VERB_INVALID, 0, 0},
162 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
163 SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
164 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
165 SP_VERB_INVALID, 0, 0},
166 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
167 SP_VERB_CONTEXT_STAR_PREFS, "tools.shapes.star", _("Style of new stars")},
168 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
169 SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", _("Style of new rectangles")},
170 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
171 SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", _("Style of new 3D boxes")},
172 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
173 SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", _("Style of new ellipses")},
174 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
175 SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral", _("Style of new spirals")},
176 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
177 SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
178 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
179 SP_VERB_CONTEXT_PEN_PREFS, "tools.freehand.pen", _("Style of new paths created by Pen")},
180 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
181 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
182 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
183 SP_VERB_INVALID, 0, 0},
184 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
185 SP_VERB_INVALID, 0, 0},
186 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
187 SP_VERB_INVALID, 0, 0},
188 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
189 SP_VERB_INVALID, 0, 0},
190 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
191 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
192 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
193 };
196 static gchar const * ui_descr =
197 "<ui>"
198 " <toolbar name='SelectToolbar'>"
199 " <toolitem action='EditSelectAll' />"
200 " <toolitem action='EditSelectAllInAllLayers' />"
201 " <toolitem action='EditDeselect' />"
202 " <separator />"
203 " <toolitem action='ObjectRotate90CCW' />"
204 " <toolitem action='ObjectRotate90' />"
205 " <toolitem action='ObjectFlipHorizontally' />"
206 " <toolitem action='ObjectFlipVertically' />"
207 " <separator />"
208 " <toolitem action='SelectionToBack' />"
209 " <toolitem action='SelectionLower' />"
210 " <toolitem action='SelectionRaise' />"
211 " <toolitem action='SelectionToFront' />"
212 " <separator />"
213 " <toolitem action='XAction' />"
214 " <toolitem action='YAction' />"
215 " <toolitem action='WidthAction' />"
216 " <toolitem action='LockAction' />"
217 " <toolitem action='HeightAction' />"
218 " <toolitem action='UnitsAction' />"
219 " <separator />"
220 " <toolitem action='transform_affect_label' />"
221 " <toolitem action='transform_stroke' />"
222 " <toolitem action='transform_corners' />"
223 " <toolitem action='transform_gradient' />"
224 " <toolitem action='transform_pattern' />"
225 " </toolbar>"
227 " <toolbar name='NodeToolbar'>"
228 " <toolitem action='NodeInsertAction' />"
229 " <toolitem action='NodeDeleteAction' />"
230 " <separator />"
231 " <toolitem action='NodeJoinAction' />"
232 " <toolitem action='NodeJoinSegmentAction' />"
233 " <toolitem action='NodeDeleteSegmentAction' />"
234 " <toolitem action='NodeBreakAction' />"
235 " <separator />"
236 " <toolitem action='NodeCuspAction' />"
237 " <toolitem action='NodeSmoothAction' />"
238 " <toolitem action='NodeSymmetricAction' />"
239 " <separator />"
240 " <toolitem action='NodeLineAction' />"
241 " <toolitem action='NodeCurveAction' />"
242 " <separator />"
243 " <toolitem action='ObjectToPath' />"
244 " <toolitem action='StrokeToPath' />"
245 " <separator />"
246 " <toolitem action='NodesShowHandlesAction' />"
247 " <separator />"
248 " <toolitem action='EditNextLPEParameterAction' />"
249 " <separator />"
250 " <toolitem action='NodeXAction' />"
251 " <toolitem action='NodeYAction' />"
252 " <toolitem action='NodeUnitsAction' />"
253 " </toolbar>"
255 " <toolbar name='TweakToolbar'>"
256 " <toolitem action='TweakWidthAction' />"
257 " <separator />"
258 " <toolitem action='TweakForceAction' />"
259 " <toolitem action='TweakPressureAction' />"
260 " <separator />"
261 " <toolitem action='TweakModeAction' />"
262 " <separator />"
263 " <toolitem action='TweakFidelityAction' />"
264 " <separator />"
265 " <toolitem action='TweakChannelsLabel' />"
266 " <toolitem action='TweakDoH' />"
267 " <toolitem action='TweakDoS' />"
268 " <toolitem action='TweakDoL' />"
269 " <toolitem action='TweakDoO' />"
270 " </toolbar>"
272 " <toolbar name='ZoomToolbar'>"
273 " <toolitem action='ZoomIn' />"
274 " <toolitem action='ZoomOut' />"
275 " <separator />"
276 " <toolitem action='Zoom1:0' />"
277 " <toolitem action='Zoom1:2' />"
278 " <toolitem action='Zoom2:1' />"
279 " <separator />"
280 " <toolitem action='ZoomSelection' />"
281 " <toolitem action='ZoomDrawing' />"
282 " <toolitem action='ZoomPage' />"
283 " <toolitem action='ZoomPageWidth' />"
284 " <separator />"
285 " <toolitem action='ZoomPrev' />"
286 " <toolitem action='ZoomNext' />"
287 " </toolbar>"
289 " <toolbar name='StarToolbar'>"
290 " <separator />"
291 " <toolitem action='StarStateAction' />"
292 " <separator />"
293 " <toolitem action='FlatAction' />"
294 " <separator />"
295 " <toolitem action='MagnitudeAction' />"
296 " <toolitem action='SpokeAction' />"
297 " <toolitem action='RoundednessAction' />"
298 " <toolitem action='RandomizationAction' />"
299 " <separator />"
300 " <toolitem action='StarResetAction' />"
301 " </toolbar>"
303 " <toolbar name='RectToolbar'>"
304 " <toolitem action='RectStateAction' />"
305 " <toolitem action='RectWidthAction' />"
306 " <toolitem action='RectHeightAction' />"
307 " <toolitem action='RadiusXAction' />"
308 " <toolitem action='RadiusYAction' />"
309 " <toolitem action='RectUnitsAction' />"
310 " <separator />"
311 " <toolitem action='RectResetAction' />"
312 " </toolbar>"
314 " <toolbar name='3DBoxToolbar'>"
315 " <toolitem action='3DBoxAngleXAction' />"
316 " <toolitem action='3DBoxVPXStateAction' />"
317 " <separator />"
318 " <toolitem action='3DBoxAngleYAction' />"
319 " <toolitem action='3DBoxVPYStateAction' />"
320 " <separator />"
321 " <toolitem action='3DBoxAngleZAction' />"
322 " <toolitem action='3DBoxVPZStateAction' />"
323 " </toolbar>"
325 " <toolbar name='SpiralToolbar'>"
326 " <toolitem action='SpiralStateAction' />"
327 " <toolitem action='SpiralRevolutionAction' />"
328 " <toolitem action='SpiralExpansionAction' />"
329 " <toolitem action='SpiralT0Action' />"
330 " <separator />"
331 " <toolitem action='SpiralResetAction' />"
332 " </toolbar>"
334 " <toolbar name='PenToolbar'>"
335 " </toolbar>"
337 " <toolbar name='PencilToolbar'>"
338 " </toolbar>"
340 " <toolbar name='CalligraphyToolbar'>"
341 " <separator />"
342 " <toolitem action='CalligraphyWidthAction' />"
343 " <toolitem action='PressureAction' />"
344 " <toolitem action='TraceAction' />"
345 " <toolitem action='ThinningAction' />"
346 " <separator />"
347 " <toolitem action='AngleAction' />"
348 " <toolitem action='TiltAction' />"
349 " <toolitem action='FixationAction' />"
350 " <separator />"
351 " <toolitem action='CapRoundingAction' />"
352 " <separator />"
353 " <toolitem action='TremorAction' />"
354 " <toolitem action='WiggleAction' />"
355 " <toolitem action='MassAction' />"
356 " <separator />"
357 " <toolitem action='CalligraphyResetAction' />"
358 " </toolbar>"
360 " <toolbar name='ArcToolbar'>"
361 " <toolitem action='ArcStateAction' />"
362 " <separator />"
363 " <toolitem action='ArcStartAction' />"
364 " <toolitem action='ArcEndAction' />"
365 " <separator />"
366 " <toolitem action='ArcOpenAction' />"
367 " <separator />"
368 " <toolitem action='ArcResetAction' />"
369 " <separator />"
370 " </toolbar>"
372 " <toolbar name='PaintbucketToolbar'>"
373 " <toolitem action='ChannelsAction' />"
374 " <separator />"
375 " <toolitem action='ThresholdAction' />"
376 " <separator />"
377 " <toolitem action='OffsetAction' />"
378 " <toolitem action='PaintbucketUnitsAction' />"
379 " <separator />"
380 " <toolitem action='AutoGapAction' />"
381 " <separator />"
382 " <toolitem action='PaintbucketResetAction' />"
383 " </toolbar>"
385 " <toolbar name='DropperToolbar'>"
386 " <toolitem action='DropperPickAlphaAction' />"
387 " <toolitem action='DropperSetAlphaAction' />"
388 " </toolbar>"
390 " <toolbar name='ConnectorToolbar'>"
391 " <toolitem action='ConnectorAvoidAction' />"
392 " <toolitem action='ConnectorIgnoreAction' />"
393 " <toolitem action='ConnectorSpacingAction' />"
394 " <toolitem action='ConnectorGraphAction' />"
395 " <toolitem action='ConnectorLengthAction' />"
396 " <toolitem action='ConnectorDirectedAction' />"
397 " <toolitem action='ConnectorOverlapAction' />"
398 " </toolbar>"
400 "</ui>"
401 ;
403 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
405 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
407 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
408 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
410 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
411 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
413 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
414 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
416 /* Global text entry widgets necessary for update */
417 /* GtkWidget *dropper_rgb_entry,
418 *dropper_opacity_entry ; */
419 // should be made a private member once this is converted to class
421 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
422 connection->disconnect();
423 delete connection;
424 }
426 static void purge_repr_listener( GObject* obj, GObject* tbl )
427 {
428 (void)obj;
429 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
430 if (oldrepr) { // remove old listener
431 sp_repr_remove_listener_by_data(oldrepr, tbl);
432 Inkscape::GC::release(oldrepr);
433 oldrepr = 0;
434 g_object_set_data( tbl, "repr", NULL );
435 }
436 }
438 GtkWidget *
439 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
440 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
441 Inkscape::UI::View::View *view, GtkTooltips *tt)
442 {
443 SPAction *action = verb->get_action(view);
444 if (!action) return NULL;
446 SPAction *doubleclick_action;
447 if (doubleclick_verb)
448 doubleclick_action = doubleclick_verb->get_action(view);
449 else
450 doubleclick_action = NULL;
452 /* fixme: Handle sensitive/unsensitive */
453 /* fixme: Implement sp_button_new_from_action */
454 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
455 gtk_widget_show(b);
456 gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
458 return b;
459 }
461 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
462 Inkscape::UI::View::View *view, GtkTooltips *tt)
463 {
464 return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
465 }
467 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
468 Inkscape::UI::View::View *view, GtkTooltips *tt)
469 {
470 return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
471 }
474 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
475 {
476 SPAction* targetAction = SP_ACTION(user_data);
477 if ( targetAction ) {
478 sp_action_perform( targetAction, NULL );
479 }
480 }
482 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
483 {
484 if ( data ) {
485 GtkAction* act = GTK_ACTION(data);
486 gtk_action_set_sensitive( act, sensitive );
487 }
488 }
490 static SPActionEventVector action_event_vector = {
491 {NULL},
492 NULL,
493 NULL,
494 sp_action_action_set_sensitive,
495 NULL,
496 NULL
497 };
499 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
500 {
501 GtkAction* act = 0;
503 SPAction* targetAction = verb->get_action(view);
504 InkAction* inky = ink_action_new( verb->get_id(), verb->get_name(), verb->get_tip(), verb->get_image(), size );
505 act = GTK_ACTION(inky);
506 gtk_action_set_sensitive( act, targetAction->sensitive );
508 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
510 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
511 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
513 return act;
514 }
516 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
517 {
518 Inkscape::UI::View::View *view = desktop;
519 gint verbsToUse[] = {
520 // disabled until we have icons for them:
521 //find
522 //SP_VERB_EDIT_TILE,
523 //SP_VERB_EDIT_UNTILE,
524 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
525 SP_VERB_DIALOG_DISPLAY,
526 SP_VERB_DIALOG_FILL_STROKE,
527 SP_VERB_DIALOG_NAMEDVIEW,
528 SP_VERB_DIALOG_TEXT,
529 SP_VERB_DIALOG_XML_EDITOR,
530 SP_VERB_EDIT_CLONE,
531 SP_VERB_EDIT_COPY,
532 SP_VERB_EDIT_CUT,
533 SP_VERB_EDIT_DUPLICATE,
534 SP_VERB_EDIT_PASTE,
535 SP_VERB_EDIT_REDO,
536 SP_VERB_EDIT_UNDO,
537 SP_VERB_EDIT_UNLINK_CLONE,
538 SP_VERB_FILE_EXPORT,
539 SP_VERB_FILE_IMPORT,
540 SP_VERB_FILE_NEW,
541 SP_VERB_FILE_OPEN,
542 SP_VERB_FILE_PRINT,
543 SP_VERB_FILE_SAVE,
544 SP_VERB_OBJECT_TO_CURVE,
545 SP_VERB_SELECTION_GROUP,
546 SP_VERB_SELECTION_OUTLINE,
547 SP_VERB_SELECTION_UNGROUP,
548 SP_VERB_ZOOM_1_1,
549 SP_VERB_ZOOM_1_2,
550 SP_VERB_ZOOM_2_1,
551 SP_VERB_ZOOM_DRAWING,
552 SP_VERB_ZOOM_IN,
553 SP_VERB_ZOOM_NEXT,
554 SP_VERB_ZOOM_OUT,
555 SP_VERB_ZOOM_PAGE,
556 SP_VERB_ZOOM_PAGE_WIDTH,
557 SP_VERB_ZOOM_PREV,
558 SP_VERB_ZOOM_SELECTION,
559 };
561 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
562 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
564 static std::map<SPDesktop*, GtkActionGroup*> groups;
565 GtkActionGroup* mainActions = 0;
566 if ( groups.find(desktop) != groups.end() ) {
567 mainActions = groups[desktop];
568 }
570 if ( !mainActions ) {
571 mainActions = gtk_action_group_new("main");
572 groups[desktop] = mainActions;
573 }
575 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
576 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
577 if ( verb ) {
578 if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
579 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
580 gtk_action_group_add_action( mainActions, act );
581 }
582 }
583 }
585 return mainActions;
586 }
589 GtkWidget *
590 sp_tool_toolbox_new()
591 {
592 GtkTooltips *tt = gtk_tooltips_new();
593 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
595 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
596 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
598 gtk_widget_set_sensitive(tb, FALSE);
600 GtkWidget *hb = gtk_handle_box_new();
601 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
602 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
603 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
605 gtk_container_add(GTK_CONTAINER(hb), tb);
606 gtk_widget_show(GTK_WIDGET(tb));
608 sigc::connection* conn = new sigc::connection;
609 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
611 return hb;
612 }
614 static void
615 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
616 {
617 g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
618 gtk_widget_queue_resize(child);
619 }
621 static void
622 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
623 {
624 g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
625 gtk_widget_queue_resize(child);
626 }
628 GtkWidget *
629 sp_aux_toolbox_new()
630 {
631 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
633 GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
634 GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
635 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
636 gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
637 gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
639 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
640 g_object_set_data(G_OBJECT(tb), "top_spacer", tb_s);
642 gtk_widget_set_sensitive(tb, FALSE);
644 GtkWidget *hb = gtk_handle_box_new();
645 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
646 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
647 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
649 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
650 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
652 gtk_container_add(GTK_CONTAINER(hb), tb);
653 gtk_widget_show(GTK_WIDGET(tb));
655 sigc::connection* conn = new sigc::connection;
656 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
658 return hb;
659 }
661 //####################################
662 //# Commands Bar
663 //####################################
665 GtkWidget *
666 sp_commands_toolbox_new()
667 {
668 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
670 GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
671 GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
672 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
673 gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
674 gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
676 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
677 gtk_widget_set_sensitive(tb, FALSE);
679 GtkWidget *hb = gtk_handle_box_new();
680 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
681 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
682 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
684 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
685 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
687 gtk_container_add(GTK_CONTAINER(hb), tb);
688 gtk_widget_show(GTK_WIDGET(tb));
690 sigc::connection* conn = new sigc::connection;
691 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
693 return hb;
694 }
696 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
697 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
698 gchar const *path, gchar const *data, gdouble def,
699 GtkWidget *focusTarget,
700 GtkWidget *us,
701 GObject *dataKludge,
702 gboolean altx, gchar const *altx_mark,
703 gdouble lower, gdouble upper, gdouble step, gdouble page,
704 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
705 void (*callback)(GtkAdjustment *, GObject *),
706 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
707 {
708 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
709 lower, upper, step, page, page ) );
710 if (us) {
711 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
712 }
714 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
716 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
717 if ( shortLabel ) {
718 g_object_set( act, "short_label", shortLabel, NULL );
719 }
721 if ( (descrCount > 0) && descrLabels && descrValues ) {
722 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
723 }
725 if ( focusTarget ) {
726 ege_adjustment_action_set_focuswidget( act, focusTarget );
727 }
729 if ( altx && altx_mark ) {
730 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
731 }
733 if ( dataKludge ) {
734 g_object_set_data( dataKludge, data, adj );
735 }
737 // Using a cast just to make sure we pass in the right kind of function pointer
738 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
740 return act;
741 }
744 //####################################
745 //# node editing callbacks
746 //####################################
748 /**
749 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
750 */
751 static ShapeEditor *get_current_shape_editor()
752 {
753 if (!SP_ACTIVE_DESKTOP) {
754 return NULL;
755 }
757 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
759 if (!SP_IS_NODE_CONTEXT(event_context)) {
760 return NULL;
761 }
763 return SP_NODE_CONTEXT(event_context)->shape_editor;
764 }
767 void
768 sp_node_path_edit_add(void)
769 {
770 ShapeEditor *shape_editor = get_current_shape_editor();
771 if (shape_editor) shape_editor->add_node();
772 }
774 void
775 sp_node_path_edit_delete(void)
776 {
777 ShapeEditor *shape_editor = get_current_shape_editor();
778 if (shape_editor) shape_editor->delete_nodes();
779 }
781 void
782 sp_node_path_edit_delete_segment(void)
783 {
784 ShapeEditor *shape_editor = get_current_shape_editor();
785 if (shape_editor) shape_editor->delete_segment();
786 }
788 void
789 sp_node_path_edit_break(void)
790 {
791 ShapeEditor *shape_editor = get_current_shape_editor();
792 if (shape_editor) shape_editor->break_at_nodes();
793 }
795 void
796 sp_node_path_edit_join(void)
797 {
798 ShapeEditor *shape_editor = get_current_shape_editor();
799 if (shape_editor) shape_editor->join_nodes();
800 }
802 void
803 sp_node_path_edit_join_segment(void)
804 {
805 ShapeEditor *shape_editor = get_current_shape_editor();
806 if (shape_editor) shape_editor->join_segments();
807 }
809 void
810 sp_node_path_edit_toline(void)
811 {
812 ShapeEditor *shape_editor = get_current_shape_editor();
813 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
814 }
816 void
817 sp_node_path_edit_tocurve(void)
818 {
819 ShapeEditor *shape_editor = get_current_shape_editor();
820 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
821 }
823 void
824 sp_node_path_edit_cusp(void)
825 {
826 ShapeEditor *shape_editor = get_current_shape_editor();
827 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
828 }
830 void
831 sp_node_path_edit_smooth(void)
832 {
833 ShapeEditor *shape_editor = get_current_shape_editor();
834 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
835 }
837 void
838 sp_node_path_edit_symmetrical(void)
839 {
840 ShapeEditor *shape_editor = get_current_shape_editor();
841 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
842 }
844 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
845 bool show = gtk_toggle_action_get_active( act );
846 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
847 ShapeEditor *shape_editor = get_current_shape_editor();
848 if (shape_editor) shape_editor->show_handles(show);
849 }
851 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
852 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
853 }
855 /* is called when the node selection is modified */
856 static void
857 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
858 {
859 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
860 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
861 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
862 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
864 // quit if run by the attr_changed listener
865 if (g_object_get_data( tbl, "freeze" )) {
866 return;
867 }
869 // in turn, prevent listener from responding
870 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
872 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
873 SPUnit const *unit = tracker->getActiveUnit();
875 ShapeEditor *shape_editor = get_current_shape_editor();
876 if (shape_editor && shape_editor->has_nodepath()) {
877 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
878 int n_selected = 0;
879 if (nodepath) {
880 n_selected = nodepath->numSelected();
881 }
883 if (n_selected == 0) {
884 gtk_action_set_sensitive(xact, FALSE);
885 gtk_action_set_sensitive(yact, FALSE);
886 } else {
887 gtk_action_set_sensitive(xact, TRUE);
888 gtk_action_set_sensitive(yact, TRUE);
889 NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
890 NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
892 if (n_selected == 1) {
893 NR::Point sel_node = nodepath->singleSelectedCoords();
894 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
895 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
896 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
897 }
898 } else {
899 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
900 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
901 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
902 /* Note: Currently x and y will always have a value, even if the coordinates of the
903 selected nodes don't coincide (in this case we use the coordinates of the center
904 of the bounding box). So the entries are never set to zero. */
905 // FIXME: Maybe we should clear the entry if several nodes are selected
906 // instead of providing a kind of average value
907 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
908 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
909 }
910 }
911 }
912 } else {
913 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
914 gtk_action_set_sensitive(xact, FALSE);
915 gtk_action_set_sensitive(yact, FALSE);
916 }
918 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
919 }
921 static void
922 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
923 {
924 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
926 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
927 SPUnit const *unit = tracker->getActiveUnit();
929 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
930 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
931 }
933 // quit if run by the attr_changed listener
934 if (g_object_get_data( tbl, "freeze" )) {
935 return;
936 }
938 // in turn, prevent listener from responding
939 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
941 ShapeEditor *shape_editor = get_current_shape_editor();
942 if (shape_editor && shape_editor->has_nodepath()) {
943 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
944 if (!strcmp(value_name, "x")) {
945 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
946 }
947 if (!strcmp(value_name, "y")) {
948 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
949 }
950 }
952 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
953 }
955 static void
956 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
957 {
958 sp_node_path_value_changed(adj, tbl, "x");
959 }
961 static void
962 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
963 {
964 sp_node_path_value_changed(adj, tbl, "y");
965 }
967 //################################
968 //## Node Editing Toolbox ##
969 //################################
971 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
972 {
973 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
974 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
975 g_object_set_data( holder, "tracker", tracker );
977 {
978 InkAction* inky = ink_action_new( "NodeInsertAction",
979 _("Insert node"),
980 _("Insert new nodes into selected segments"),
981 "node_insert",
982 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
983 g_object_set( inky, "short_label", _("Insert"), NULL );
984 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
985 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
986 }
988 {
989 InkAction* inky = ink_action_new( "NodeDeleteAction",
990 _("Delete node"),
991 _("Delete selected nodes"),
992 "node_delete",
993 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
994 g_object_set( inky, "short_label", _("Delete"), NULL );
995 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
996 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
997 }
999 {
1000 InkAction* inky = ink_action_new( "NodeJoinAction",
1001 _("Join endnodes"),
1002 _("Join selected endnodes"),
1003 "node_join",
1004 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1005 g_object_set( inky, "short_label", _("Join"), NULL );
1006 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1007 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1008 }
1010 {
1011 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1012 _("Join Segment"),
1013 _("Join selected endnodes with a new segment"),
1014 "node_join_segment",
1015 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1016 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1017 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1018 }
1020 {
1021 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1022 _("Delete Segment"),
1023 _("Split path between two non-endpoint nodes"),
1024 "node_delete_segment",
1025 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1026 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1027 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1028 }
1030 {
1031 InkAction* inky = ink_action_new( "NodeBreakAction",
1032 _("Node Break"),
1033 _("Break path at selected nodes"),
1034 "node_break",
1035 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1036 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1037 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1038 }
1040 {
1041 InkAction* inky = ink_action_new( "NodeCuspAction",
1042 _("Node Cusp"),
1043 _("Make selected nodes corner"),
1044 "node_cusp",
1045 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1046 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1047 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1048 }
1050 {
1051 InkAction* inky = ink_action_new( "NodeSmoothAction",
1052 _("Node Smooth"),
1053 _("Make selected nodes smooth"),
1054 "node_smooth",
1055 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1056 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1057 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1058 }
1060 {
1061 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1062 _("Node Symmetric"),
1063 _("Make selected nodes symmetric"),
1064 "node_symmetric",
1065 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1066 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1067 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1068 }
1070 {
1071 InkAction* inky = ink_action_new( "NodeLineAction",
1072 _("Node Line"),
1073 _("Make selected segments lines"),
1074 "node_line",
1075 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1076 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1077 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1078 }
1080 {
1081 InkAction* inky = ink_action_new( "NodeCurveAction",
1082 _("Node Curve"),
1083 _("Make selected segments curves"),
1084 "node_curve",
1085 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1086 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1087 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1088 }
1090 {
1091 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1092 _("Show Handles"),
1093 _("Show the Bezier handles of selected nodes"),
1094 "nodes_show_handles",
1095 Inkscape::ICON_SIZE_DECORATION );
1096 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1097 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1098 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1099 }
1101 {
1102 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1103 _("Next Path Effect Parameter"),
1104 _("Show next Path Effect parameter for editing"),
1105 "edit_next_parameter",
1106 Inkscape::ICON_SIZE_DECORATION );
1107 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1108 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1109 }
1111 /* X coord of selected node(s) */
1112 {
1113 EgeAdjustmentAction* eact = 0;
1114 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1115 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1116 eact = create_adjustment_action( "NodeXAction",
1117 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1118 "tools.nodes", "Xcoord", 0,
1119 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1120 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1121 labels, values, G_N_ELEMENTS(labels),
1122 sp_node_path_x_value_changed );
1123 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1124 g_object_set_data( holder, "nodes_x_action", eact );
1125 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1126 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1127 }
1129 /* Y coord of selected node(s) */
1130 {
1131 EgeAdjustmentAction* eact = 0;
1132 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1133 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1134 eact = create_adjustment_action( "NodeYAction",
1135 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1136 "tools.nodes", "Ycoord", 0,
1137 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1138 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1139 labels, values, G_N_ELEMENTS(labels),
1140 sp_node_path_y_value_changed );
1141 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1142 g_object_set_data( holder, "nodes_y_action", eact );
1143 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1144 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1145 }
1147 // add the units menu
1148 {
1149 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1150 gtk_action_group_add_action( mainActions, act );
1151 }
1153 sigc::connection *connection = new sigc::connection (
1154 desktop->connectToolSubselectionChanged(sigc::bind (sigc::ptr_fun(sp_node_toolbox_coord_changed), (GObject *)holder))
1155 );
1157 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1158 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1159 } // end of sp_node_toolbox_prep()
1162 //########################
1163 //## Zoom Toolbox ##
1164 //########################
1166 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1167 {
1168 // no custom GtkAction setup needed
1169 } // end of sp_zoom_toolbox_prep()
1171 void
1172 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1173 {
1174 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")));
1175 }
1178 void
1179 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1180 {
1181 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")));
1182 }
1184 void
1185 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1186 {
1187 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")));
1188 }
1190 static void
1191 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1192 {
1193 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1194 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1196 if (old_desktop) {
1197 GList *children, *iter;
1199 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1200 for ( iter = children ; iter ; iter = iter->next ) {
1201 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1202 }
1203 g_list_free(children);
1204 }
1206 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1208 if (desktop) {
1209 gtk_widget_set_sensitive(toolbox, TRUE);
1210 setup_func(toolbox, desktop);
1211 update_func(desktop, desktop->event_context, toolbox);
1212 *conn = desktop->connectEventContextChanged
1213 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1214 } else {
1215 gtk_widget_set_sensitive(toolbox, FALSE);
1216 }
1218 } // end of toolbox_set_desktop()
1221 static void
1222 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1223 {
1224 GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1225 gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1226 if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1227 // "toolbox.tools" was not set. Fallback to older value
1228 shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1230 // Copy the setting forwards
1231 prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1232 }
1233 Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1235 for (int i = 0 ; tools[i].type_name ; i++ ) {
1236 GtkWidget *button =
1237 sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1238 SP_BUTTON_TYPE_TOGGLE,
1239 Inkscape::Verb::get(tools[i].verb),
1240 Inkscape::Verb::get(tools[i].doubleclick_verb),
1241 desktop,
1242 tooltips );
1244 g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1245 (gpointer)button );
1246 }
1247 }
1250 static void
1251 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1252 {
1253 gchar const *const tname = ( eventcontext
1254 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1255 : NULL );
1256 for (int i = 0 ; tools[i].type_name ; i++ ) {
1257 SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1258 sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1259 }
1260 }
1262 static void
1263 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1264 {
1265 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1266 GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1267 GtkUIManager* mgr = gtk_ui_manager_new();
1268 GError* errVal = 0;
1269 gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1270 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1272 std::map<std::string, GtkWidget*> dataHolders;
1274 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1275 if ( aux_toolboxes[i].prep_func ) {
1276 // converted to GtkActions and UIManager
1278 GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1279 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1280 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1281 dataHolders[aux_toolboxes[i].type_name] = kludge;
1282 aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1283 } else {
1285 GtkWidget *sub_toolbox = 0;
1286 if (aux_toolboxes[i].create_func == NULL)
1287 sub_toolbox = sp_empty_toolbox_new(desktop);
1288 else {
1289 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1290 }
1292 gtk_size_group_add_widget( grouper, sub_toolbox );
1294 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1295 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1297 }
1298 }
1300 // Second pass to create toolbars *after* all GtkActions are created
1301 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1302 if ( aux_toolboxes[i].prep_func ) {
1303 // converted to GtkActions and UIManager
1305 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1307 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1308 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1310 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1311 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1312 g_free( tmp );
1313 tmp = 0;
1315 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1316 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1317 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1318 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1319 }
1320 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1323 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1325 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1326 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1327 swatch->setDesktop( desktop );
1328 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1329 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1330 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1331 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 );
1332 }
1334 gtk_widget_show_all( holder );
1335 sp_set_font_size_smaller( holder );
1337 gtk_size_group_add_widget( grouper, holder );
1339 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1340 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1341 }
1342 }
1344 g_object_unref( G_OBJECT(grouper) );
1345 }
1347 static void
1348 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1349 {
1350 gchar const *tname = ( eventcontext
1351 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1352 : NULL );
1353 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1354 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1355 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1356 gtk_widget_show_all(sub_toolbox);
1357 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1358 } else {
1359 gtk_widget_hide(sub_toolbox);
1360 }
1361 }
1362 }
1364 static void
1365 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1366 {
1367 gchar const * descr =
1368 "<ui>"
1369 " <toolbar name='CommandsToolbar'>"
1370 " <toolitem action='FileNew' />"
1371 " <toolitem action='FileOpen' />"
1372 " <toolitem action='FileSave' />"
1373 " <toolitem action='FilePrint' />"
1374 " <separator />"
1375 " <toolitem action='FileImport' />"
1376 " <toolitem action='FileExport' />"
1377 " <separator />"
1378 " <toolitem action='EditUndo' />"
1379 " <toolitem action='EditRedo' />"
1380 " <separator />"
1381 " <toolitem action='EditCopy' />"
1382 " <toolitem action='EditCut' />"
1383 " <toolitem action='EditPaste' />"
1384 " <separator />"
1385 " <toolitem action='ZoomSelection' />"
1386 " <toolitem action='ZoomDrawing' />"
1387 " <toolitem action='ZoomPage' />"
1388 " <separator />"
1389 " <toolitem action='EditDuplicate' />"
1390 " <toolitem action='EditClone' />"
1391 " <toolitem action='EditUnlinkClone' />"
1392 " <separator />"
1393 " <toolitem action='SelectionGroup' />"
1394 " <toolitem action='SelectionUnGroup' />"
1395 " <separator />"
1396 " <toolitem action='DialogFillStroke' />"
1397 " <toolitem action='DialogText' />"
1398 " <toolitem action='DialogXMLEditor' />"
1399 " <toolitem action='DialogAlignDistribute' />"
1400 " <separator />"
1401 " <toolitem action='DialogPreferences' />"
1402 " <toolitem action='DialogDocumentProperties' />"
1403 " </toolbar>"
1404 "</ui>";
1405 GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1408 GtkUIManager* mgr = gtk_ui_manager_new();
1409 GError* errVal = 0;
1411 gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1412 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1414 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1415 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1416 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1417 }
1418 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1419 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1420 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1423 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1424 }
1426 static void
1427 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1428 {
1429 }
1431 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1432 {
1433 gtk_widget_show(toolbox_toplevel);
1434 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1436 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1437 if (!shown_toolbox) {
1438 return;
1439 }
1440 gtk_widget_show(toolbox);
1442 // need to show the spacer, or the padding will be off
1443 GtkWidget *spacer = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "top_spacer"));
1444 gtk_widget_show(spacer);
1446 gtk_widget_show_all(shown_toolbox);
1447 }
1449 void
1450 aux_toolbox_space(GtkWidget *tb, gint space)
1451 {
1452 gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1453 }
1455 static GtkWidget *
1456 sp_empty_toolbox_new(SPDesktop *desktop)
1457 {
1458 GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1459 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1460 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1462 gtk_widget_show_all(tbl);
1463 sp_set_font_size_smaller (tbl);
1465 return tbl;
1466 }
1468 // helper UI functions
1470 GtkWidget *
1471 sp_tb_spinbutton(
1472 gchar *label, gchar const *tooltip,
1473 gchar const *path, gchar const *data, gdouble def,
1474 GtkWidget *us,
1475 GtkWidget *tbl,
1476 gboolean altx, gchar const *altx_mark,
1477 gdouble lower, gdouble upper, gdouble step, gdouble page,
1478 void (*callback)(GtkAdjustment *, GtkWidget *),
1479 gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1480 {
1481 GtkTooltips *tt = gtk_tooltips_new();
1483 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1485 GtkWidget *l = gtk_label_new(label);
1486 gtk_widget_show(l);
1487 gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1488 gtk_container_add(GTK_CONTAINER(hb), l);
1490 GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1491 lower, upper, step, page, page);
1492 gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1493 if (us)
1494 sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1496 GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1497 gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1498 if (altx)
1499 gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1500 gtk_widget_set_size_request(sb,
1501 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1502 AUX_SPINBUTTON_HEIGHT);
1503 gtk_widget_show(sb);
1504 gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1505 gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1506 gtk_container_add(GTK_CONTAINER(hb), sb);
1507 gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1509 return hb;
1510 }
1512 #define MODE_LABEL_WIDTH 70
1514 //########################
1515 //## Star ##
1516 //########################
1518 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1519 {
1520 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1522 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1523 // do not remember prefs if this call is initiated by an undo change, because undoing object
1524 // creation sets bogus values to its attributes before it is deleted
1525 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1526 }
1528 // quit if run by the attr_changed listener
1529 if (g_object_get_data( dataKludge, "freeze" )) {
1530 return;
1531 }
1533 // in turn, prevent listener from responding
1534 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1536 bool modmade = false;
1538 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1539 GSList const *items = selection->itemList();
1540 for (; items != NULL; items = items->next) {
1541 if (SP_IS_STAR((SPItem *) items->data)) {
1542 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1543 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1544 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1545 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1546 + M_PI / (gint)adj->value));
1547 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1548 modmade = true;
1549 }
1550 }
1551 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1552 _("Star: Change number of corners"));
1554 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1555 }
1557 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1558 {
1559 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1561 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1562 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1563 }
1565 // quit if run by the attr_changed listener
1566 if (g_object_get_data( dataKludge, "freeze" )) {
1567 return;
1568 }
1570 // in turn, prevent listener from responding
1571 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1573 bool modmade = false;
1574 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1575 GSList const *items = selection->itemList();
1576 for (; items != NULL; items = items->next) {
1577 if (SP_IS_STAR((SPItem *) items->data)) {
1578 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1580 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1581 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1582 if (r2 < r1) {
1583 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1584 } else {
1585 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1586 }
1588 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1589 modmade = true;
1590 }
1591 }
1593 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1594 _("Star: Change spoke ratio"));
1596 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1597 }
1599 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1600 {
1601 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1602 bool flat = ege_select_one_action_get_active( act ) == 0;
1604 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1605 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1606 flat ? "true" : "false" );
1607 }
1609 // quit if run by the attr_changed listener
1610 if (g_object_get_data( dataKludge, "freeze" )) {
1611 return;
1612 }
1614 // in turn, prevent listener from responding
1615 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1617 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1618 GSList const *items = selection->itemList();
1619 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1620 bool modmade = false;
1622 if ( prop_action ) {
1623 gtk_action_set_sensitive( prop_action, !flat );
1624 }
1626 for (; items != NULL; items = items->next) {
1627 if (SP_IS_STAR((SPItem *) items->data)) {
1628 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1629 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1630 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1631 modmade = true;
1632 }
1633 }
1635 if (modmade) {
1636 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1637 flat ? _("Make polygon") : _("Make star"));
1638 }
1640 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1641 }
1643 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1644 {
1645 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1647 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1648 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1649 }
1651 // quit if run by the attr_changed listener
1652 if (g_object_get_data( dataKludge, "freeze" )) {
1653 return;
1654 }
1656 // in turn, prevent listener from responding
1657 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1659 bool modmade = false;
1661 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1662 GSList const *items = selection->itemList();
1663 for (; items != NULL; items = items->next) {
1664 if (SP_IS_STAR((SPItem *) items->data)) {
1665 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1666 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1667 SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1668 modmade = true;
1669 }
1670 }
1671 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1672 _("Star: Change rounding"));
1674 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1675 }
1677 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1678 {
1679 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1681 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1682 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1683 }
1685 // quit if run by the attr_changed listener
1686 if (g_object_get_data( dataKludge, "freeze" )) {
1687 return;
1688 }
1690 // in turn, prevent listener from responding
1691 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1693 bool modmade = false;
1695 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1696 GSList const *items = selection->itemList();
1697 for (; items != NULL; items = items->next) {
1698 if (SP_IS_STAR((SPItem *) items->data)) {
1699 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1700 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1701 SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1702 modmade = true;
1703 }
1704 }
1705 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1706 _("Star: Change randomization"));
1708 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1709 }
1712 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1713 gchar const */*old_value*/, gchar const */*new_value*/,
1714 bool /*is_interactive*/, gpointer data)
1715 {
1716 GtkWidget *tbl = GTK_WIDGET(data);
1718 // quit if run by the _changed callbacks
1719 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1720 return;
1721 }
1723 // in turn, prevent callbacks from responding
1724 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1726 GtkAdjustment *adj = 0;
1728 if (!strcmp(name, "inkscape:randomized")) {
1729 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1730 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1731 } else if (!strcmp(name, "inkscape:rounded")) {
1732 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1733 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1734 } else if (!strcmp(name, "inkscape:flatsided")) {
1735 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1736 char const *flatsides = repr->attribute("inkscape:flatsided");
1737 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1738 if ( flatsides && !strcmp(flatsides,"false") ) {
1739 ege_select_one_action_set_active( flat_action, 1 );
1740 gtk_action_set_sensitive( prop_action, TRUE );
1741 } else {
1742 ege_select_one_action_set_active( flat_action, 0 );
1743 gtk_action_set_sensitive( prop_action, FALSE );
1744 }
1745 } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
1746 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1747 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1748 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1749 if (r2 < r1) {
1750 gtk_adjustment_set_value(adj, r2/r1);
1751 } else {
1752 gtk_adjustment_set_value(adj, r1/r2);
1753 }
1754 } else if (!strcmp(name, "sodipodi:sides")) {
1755 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1756 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1757 }
1759 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1760 }
1763 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1764 {
1765 NULL, /* child_added */
1766 NULL, /* child_removed */
1767 star_tb_event_attr_changed,
1768 NULL, /* content_changed */
1769 NULL /* order_changed */
1770 };
1773 /**
1774 * \param selection Should not be NULL.
1775 */
1776 static void
1777 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1778 {
1779 int n_selected = 0;
1780 Inkscape::XML::Node *repr = NULL;
1782 purge_repr_listener( tbl, tbl );
1784 for (GSList const *items = selection->itemList();
1785 items != NULL;
1786 items = items->next)
1787 {
1788 if (SP_IS_STAR((SPItem *) items->data)) {
1789 n_selected++;
1790 repr = SP_OBJECT_REPR((SPItem *) items->data);
1791 }
1792 }
1794 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1796 if (n_selected == 0) {
1797 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1798 } else if (n_selected == 1) {
1799 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1801 if (repr) {
1802 g_object_set_data( tbl, "repr", repr );
1803 Inkscape::GC::anchor(repr);
1804 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1805 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1806 }
1807 } else {
1808 // FIXME: implement averaging of all parameters for multiple selected stars
1809 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1810 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1811 }
1812 }
1815 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1816 {
1817 // FIXME: in this and all other _default functions, set some flag telling the value_changed
1818 // callbacks to lump all the changes for all selected objects in one undo step
1820 GtkAdjustment *adj = 0;
1822 // fixme: make settable in prefs!
1823 gint mag = 5;
1824 gdouble prop = 0.5;
1825 gboolean flat = FALSE;
1826 gdouble randomized = 0;
1827 gdouble rounded = 0;
1829 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1830 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1832 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1833 gtk_action_set_sensitive( sb2, !flat );
1835 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1836 gtk_adjustment_set_value(adj, mag);
1837 gtk_adjustment_value_changed(adj);
1839 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1840 gtk_adjustment_set_value(adj, prop);
1841 gtk_adjustment_value_changed(adj);
1843 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1844 gtk_adjustment_set_value(adj, rounded);
1845 gtk_adjustment_value_changed(adj);
1847 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1848 gtk_adjustment_set_value(adj, randomized);
1849 gtk_adjustment_value_changed(adj);
1850 }
1853 void
1854 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1855 {
1856 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1857 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1858 GtkWidget *l = gtk_label_new(NULL);
1859 gtk_label_set_markup(GTK_LABEL(l), title);
1860 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1861 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1862 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1863 }
1866 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1867 {
1868 {
1869 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1870 ege_output_action_set_use_markup( act, TRUE );
1871 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1872 g_object_set_data( holder, "mode_action", act );
1873 }
1875 {
1876 EgeAdjustmentAction* eact = 0;
1877 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1878 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1880 /* Flatsided checkbox */
1881 {
1882 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1884 GtkTreeIter iter;
1885 gtk_list_store_append( model, &iter );
1886 gtk_list_store_set( model, &iter,
1887 0, _("Polygon"),
1888 1, _("Regular polygon (with one handle) instead of a star"),
1889 2, "star_flat",
1890 -1 );
1892 gtk_list_store_append( model, &iter );
1893 gtk_list_store_set( model, &iter,
1894 0, _("Star"),
1895 1, _("Star instead of a regular polygon (with one handle)"),
1896 2, "star_angled",
1897 -1 );
1899 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
1900 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1901 g_object_set_data( holder, "flat_action", act );
1903 ege_select_one_action_set_appearance( act, "full" );
1904 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1905 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1906 ege_select_one_action_set_icon_column( act, 2 );
1907 ege_select_one_action_set_tooltip_column( act, 1 );
1909 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1910 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1911 }
1913 /* Magnitude */
1914 {
1915 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1916 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1917 eact = create_adjustment_action( "MagnitudeAction",
1918 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
1919 "tools.shapes.star", "magnitude", 3,
1920 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1921 3, 1024, 1, 5,
1922 labels, values, G_N_ELEMENTS(labels),
1923 sp_stb_magnitude_value_changed,
1924 1.0, 0 );
1925 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1926 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1927 }
1929 /* Spoke ratio */
1930 {
1931 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1932 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1933 eact = create_adjustment_action( "SpokeAction",
1934 _("Spoke ratio"), _("Spoke ratio:"),
1935 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1936 // Base radius is the same for the closest handle.
1937 _("Base radius to tip radius ratio"),
1938 "tools.shapes.star", "proportion", 0.5,
1939 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1940 0.01, 1.0, 0.01, 0.1,
1941 labels, values, G_N_ELEMENTS(labels),
1942 sp_stb_proportion_value_changed );
1943 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1944 g_object_set_data( holder, "prop_action", eact );
1945 }
1947 if ( !isFlatSided ) {
1948 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1949 } else {
1950 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1951 }
1953 /* Roundedness */
1954 {
1955 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1956 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1957 eact = create_adjustment_action( "RoundednessAction",
1958 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1959 "tools.shapes.star", "rounded", 0.0,
1960 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1961 -10.0, 10.0, 0.01, 0.1,
1962 labels, values, G_N_ELEMENTS(labels),
1963 sp_stb_rounded_value_changed );
1964 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1965 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1966 }
1968 /* Randomization */
1969 {
1970 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1971 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1972 eact = create_adjustment_action( "RandomizationAction",
1973 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
1974 "tools.shapes.star", "randomized", 0.0,
1975 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1976 -10.0, 10.0, 0.001, 0.01,
1977 labels, values, G_N_ELEMENTS(labels),
1978 sp_stb_randomized_value_changed, 0.1, 3 );
1979 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1980 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1981 }
1982 }
1984 {
1985 /* Reset */
1986 {
1987 GtkAction* act = gtk_action_new( "StarResetAction",
1988 _("Defaults"),
1989 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1990 GTK_STOCK_CLEAR );
1991 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1992 gtk_action_group_add_action( mainActions, act );
1993 gtk_action_set_sensitive( act, TRUE );
1994 }
1995 }
1997 sigc::connection *connection = new sigc::connection(
1998 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
1999 );
2000 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2001 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2002 }
2005 //########################
2006 //## Rect ##
2007 //########################
2009 static void sp_rtb_sensitivize( GObject *tbl )
2010 {
2011 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2012 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2013 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2015 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2016 gtk_action_set_sensitive( not_rounded, FALSE );
2017 } else {
2018 gtk_action_set_sensitive( not_rounded, TRUE );
2019 }
2020 }
2023 static void
2024 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2025 void (*setter)(SPRect *, gdouble))
2026 {
2027 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2029 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2030 SPUnit const *unit = tracker->getActiveUnit();
2032 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2033 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2034 }
2036 // quit if run by the attr_changed listener
2037 if (g_object_get_data( tbl, "freeze" )) {
2038 return;
2039 }
2041 // in turn, prevent listener from responding
2042 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2044 bool modmade = false;
2045 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2046 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2047 if (SP_IS_RECT(items->data)) {
2048 if (adj->value != 0) {
2049 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2050 } else {
2051 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2052 }
2053 modmade = true;
2054 }
2055 }
2057 sp_rtb_sensitivize( tbl );
2059 if (modmade) {
2060 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2061 _("Change rectangle"));
2062 }
2064 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2065 }
2067 static void
2068 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2069 {
2070 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2071 }
2073 static void
2074 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2075 {
2076 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2077 }
2079 static void
2080 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2081 {
2082 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2083 }
2085 static void
2086 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2087 {
2088 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2089 }
2093 static void
2094 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2095 {
2096 GtkAdjustment *adj = 0;
2098 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2099 gtk_adjustment_set_value(adj, 0.0);
2100 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2101 gtk_adjustment_value_changed(adj);
2103 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2104 gtk_adjustment_set_value(adj, 0.0);
2105 gtk_adjustment_value_changed(adj);
2107 sp_rtb_sensitivize( obj );
2108 }
2110 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2111 gchar const */*old_value*/, gchar const */*new_value*/,
2112 bool /*is_interactive*/, gpointer data)
2113 {
2114 GObject *tbl = G_OBJECT(data);
2116 // quit if run by the _changed callbacks
2117 if (g_object_get_data( tbl, "freeze" )) {
2118 return;
2119 }
2121 // in turn, prevent callbacks from responding
2122 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2124 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2125 SPUnit const *unit = tracker->getActiveUnit();
2127 gpointer item = g_object_get_data( tbl, "item" );
2128 if (item && SP_IS_RECT(item)) {
2129 {
2130 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2131 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2132 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2133 }
2135 {
2136 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2137 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2138 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2139 }
2141 {
2142 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2143 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2144 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2145 }
2147 {
2148 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2149 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2150 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2151 }
2152 }
2154 sp_rtb_sensitivize( tbl );
2156 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2157 }
2160 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2161 NULL, /* child_added */
2162 NULL, /* child_removed */
2163 rect_tb_event_attr_changed,
2164 NULL, /* content_changed */
2165 NULL /* order_changed */
2166 };
2168 /**
2169 * \param selection should not be NULL.
2170 */
2171 static void
2172 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2173 {
2174 int n_selected = 0;
2175 Inkscape::XML::Node *repr = NULL;
2176 SPItem *item = NULL;
2178 if ( g_object_get_data( tbl, "repr" ) ) {
2179 g_object_set_data( tbl, "item", NULL );
2180 }
2181 purge_repr_listener( tbl, tbl );
2183 for (GSList const *items = selection->itemList();
2184 items != NULL;
2185 items = items->next) {
2186 if (SP_IS_RECT((SPItem *) items->data)) {
2187 n_selected++;
2188 item = (SPItem *) items->data;
2189 repr = SP_OBJECT_REPR(item);
2190 }
2191 }
2193 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2195 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2197 if (n_selected == 0) {
2198 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2200 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2201 gtk_action_set_sensitive(w, FALSE);
2202 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2203 gtk_action_set_sensitive(h, FALSE);
2205 } else if (n_selected == 1) {
2206 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2207 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2209 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2210 gtk_action_set_sensitive(w, TRUE);
2211 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2212 gtk_action_set_sensitive(h, TRUE);
2214 if (repr) {
2215 g_object_set_data( tbl, "repr", repr );
2216 g_object_set_data( tbl, "item", item );
2217 Inkscape::GC::anchor(repr);
2218 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2219 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2220 }
2221 } else {
2222 // FIXME: implement averaging of all parameters for multiple selected
2223 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2224 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2225 sp_rtb_sensitivize( tbl );
2226 }
2227 }
2230 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2231 {
2232 EgeAdjustmentAction* eact = 0;
2234 {
2235 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2236 ege_output_action_set_use_markup( act, TRUE );
2237 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2238 g_object_set_data( holder, "mode_action", act );
2239 }
2241 // rx/ry units menu: create
2242 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2243 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2244 // fixme: add % meaning per cent of the width/height
2245 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2246 g_object_set_data( holder, "tracker", tracker );
2248 /* W */
2249 {
2250 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2251 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2252 eact = create_adjustment_action( "RectWidthAction",
2253 _("Width"), _("W:"), _("Width of rectangle"),
2254 "tools.shapes.rect", "width", 0,
2255 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2256 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2257 labels, values, G_N_ELEMENTS(labels),
2258 sp_rtb_width_value_changed );
2259 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2260 g_object_set_data( holder, "width_action", eact );
2261 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2262 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2263 }
2265 /* H */
2266 {
2267 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2268 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2269 eact = create_adjustment_action( "RectHeightAction",
2270 _("Height"), _("H:"), _("Height of rectangle"),
2271 "tools.shapes.rect", "height", 0,
2272 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2273 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2274 labels, values, G_N_ELEMENTS(labels),
2275 sp_rtb_height_value_changed );
2276 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2277 g_object_set_data( holder, "height_action", eact );
2278 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2279 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2280 }
2282 /* rx */
2283 {
2284 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2285 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2286 eact = create_adjustment_action( "RadiusXAction",
2287 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2288 "tools.shapes.rect", "rx", 0,
2289 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2290 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2291 labels, values, G_N_ELEMENTS(labels),
2292 sp_rtb_rx_value_changed);
2293 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2294 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2295 }
2297 /* ry */
2298 {
2299 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2300 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2301 eact = create_adjustment_action( "RadiusYAction",
2302 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2303 "tools.shapes.rect", "ry", 0,
2304 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2305 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2306 labels, values, G_N_ELEMENTS(labels),
2307 sp_rtb_ry_value_changed);
2308 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2309 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2310 }
2312 // add the units menu
2313 {
2314 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2315 gtk_action_group_add_action( mainActions, act );
2316 }
2318 /* Reset */
2319 {
2320 InkAction* inky = ink_action_new( "RectResetAction",
2321 _("Not rounded"),
2322 _("Make corners sharp"),
2323 "squared_corner",
2324 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2325 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2326 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2327 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2328 g_object_set_data( holder, "not_rounded", inky );
2329 }
2331 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2332 sp_rtb_sensitivize( holder );
2334 sigc::connection *connection = new sigc::connection(
2335 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2336 );
2337 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2338 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2339 }
2341 //########################
2342 //## 3D Box ##
2343 //########################
2345 // normalize angle so that it lies in the interval [0,360]
2346 static double box3d_normalize_angle (double a) {
2347 double angle = a + ((int) (a/360.0))*360;
2348 if (angle < 0) {
2349 angle += 360.0;
2350 }
2351 return angle;
2352 }
2354 static void
2355 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2356 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2357 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2358 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2359 // are reset).
2360 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2362 if (is_infinite) {
2363 gtk_toggle_action_set_active(tact, TRUE);
2364 gtk_action_set_sensitive(act, TRUE);
2366 double angle = persp3d_get_infinite_angle(persp, axis);
2367 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2368 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2369 }
2370 } else {
2371 gtk_toggle_action_set_active(tact, FALSE);
2372 gtk_action_set_sensitive(act, FALSE);
2373 }
2374 }
2376 static void
2377 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2378 if (!persp_repr) {
2379 g_print ("No perspective given to box3d_resync_toolbar().\n");
2380 return;
2381 }
2383 GtkWidget *tbl = GTK_WIDGET(data);
2384 GtkAdjustment *adj = 0;
2385 GtkAction *act = 0;
2386 GtkToggleAction *tact = 0;
2387 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2388 {
2389 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2390 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2391 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2393 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2394 }
2395 {
2396 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2397 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2398 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2400 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2401 }
2402 {
2403 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2404 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2405 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2407 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2408 }
2409 }
2411 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2412 gchar const */*old_value*/, gchar const */*new_value*/,
2413 bool /*is_interactive*/, gpointer data)
2414 {
2415 GtkWidget *tbl = GTK_WIDGET(data);
2417 // quit if run by the attr_changed listener
2418 // note: it used to work without the differently called freeze_ attributes (here and in
2419 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2420 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2421 return;
2422 }
2424 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2425 // sp_document_maybe_done() when the document is undo insensitive)
2426 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2428 // TODO: Only update the appropriate part of the toolbar
2429 // if (!strcmp(name, "inkscape:vp_z")) {
2430 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2431 // }
2433 Persp3D *persp = persp3d_get_from_repr(repr);
2434 persp3d_update_box_reprs(persp);
2436 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2437 }
2439 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2440 {
2441 NULL, /* child_added */
2442 NULL, /* child_removed */
2443 box3d_persp_tb_event_attr_changed,
2444 NULL, /* content_changed */
2445 NULL /* order_changed */
2446 };
2448 /**
2449 * \param selection Should not be NULL.
2450 */
2451 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2452 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2453 static void
2454 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2455 {
2456 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2457 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2458 // update the perspectives with infinite VPs and leave the other ones untouched).
2460 Inkscape::XML::Node *persp_repr = NULL;
2461 purge_repr_listener(tbl, tbl);
2463 SPItem *item = selection->singleItem();
2464 if (item && SP_IS_BOX3D(item)) {
2465 // FIXME: Also deal with multiple selected boxes
2466 SPBox3D *box = SP_BOX3D(item);
2467 Persp3D *persp = box3d_get_perspective(box);
2468 persp_repr = SP_OBJECT_REPR(persp);
2469 if (persp_repr) {
2470 g_object_set_data(tbl, "repr", persp_repr);
2471 Inkscape::GC::anchor(persp_repr);
2472 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2473 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2474 }
2476 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2477 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2479 box3d_resync_toolbar(persp_repr, tbl);
2480 }
2481 }
2483 static void
2484 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2485 {
2486 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2487 SPDocument *document = sp_desktop_document(desktop);
2489 // quit if run by the attr_changed listener
2490 // note: it used to work without the differently called freeze_ attributes (here and in
2491 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2492 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2493 return;
2494 }
2496 // in turn, prevent listener from responding
2497 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2499 //Persp3D *persp = document->current_persp3d;
2500 std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
2501 if (sel_persps.empty()) {
2502 // this can happen when the document is created; we silently ignore it
2503 return;
2504 }
2505 Persp3D *persp = *(sel_persps.begin());
2507 persp->tmat.set_infinite_direction (axis, adj->value);
2508 SP_OBJECT(persp)->updateRepr();
2510 // TODO: use the correct axis here, too
2511 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2513 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2514 }
2517 static void
2518 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2519 {
2520 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2521 }
2523 static void
2524 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2525 {
2526 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2527 }
2529 static void
2530 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2531 {
2532 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2533 }
2536 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction *box3d_angle, Proj::Axis axis )
2537 {
2538 // TODO: Take all selected perspectives into account
2539 std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
2540 if (sel_persps.empty()) {
2541 // this can happen when the document is created; we silently ignore it
2542 return;
2543 }
2544 Persp3D *persp = *(sel_persps.begin());
2546 bool set_infinite = gtk_toggle_action_get_active(act);
2547 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2548 }
2550 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2551 {
2552 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2553 }
2555 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2556 {
2557 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2558 }
2560 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2561 {
2562 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2563 }
2565 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2566 {
2567 EgeAdjustmentAction* eact = 0;
2568 SPDocument *document = sp_desktop_document (desktop);
2569 Persp3D *persp = document->current_persp3d;
2571 EgeAdjustmentAction* box3d_angle_x = 0;
2572 EgeAdjustmentAction* box3d_angle_y = 0;
2573 EgeAdjustmentAction* box3d_angle_z = 0;
2575 /* Angle X */
2576 {
2577 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2578 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2579 eact = create_adjustment_action( "3DBoxAngleXAction",
2580 _("Angle in X direction"), _("Angle X:"),
2581 // Translators: PL is short for 'perspective line'
2582 _("Angle of PLs in X direction"),
2583 "tools.shapes.3dbox", "box3d_angle_x", 30,
2584 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2585 -360.0, 360.0, 1.0, 10.0,
2586 labels, values, G_N_ELEMENTS(labels),
2587 box3d_angle_x_value_changed );
2588 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2589 g_object_set_data( holder, "box3d_angle_x_action", eact );
2590 box3d_angle_x = eact;
2591 }
2593 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2594 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2595 } else {
2596 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2597 }
2600 /* VP X state */
2601 {
2602 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2603 // Translators: VP is short for 'vanishing point'
2604 _("State of VP in X direction"),
2605 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2606 "toggle_vp_x",
2607 Inkscape::ICON_SIZE_DECORATION );
2608 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2609 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2610 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2611 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2612 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2613 }
2615 /* Angle Y */
2616 {
2617 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2618 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2619 eact = create_adjustment_action( "3DBoxAngleYAction",
2620 _("Angle in Y direction"), _("Angle Y:"),
2621 // Translators: PL is short for 'perspective line'
2622 _("Angle of PLs in Y direction"),
2623 "tools.shapes.3dbox", "box3d_angle_y", 30,
2624 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2625 -360.0, 360.0, 1.0, 10.0,
2626 labels, values, G_N_ELEMENTS(labels),
2627 box3d_angle_y_value_changed );
2628 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2629 g_object_set_data( holder, "box3d_angle_y_action", eact );
2630 box3d_angle_y = eact;
2631 }
2633 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2634 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2635 } else {
2636 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2637 }
2639 /* VP Y state */
2640 {
2641 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2642 // Translators: VP is short for 'vanishing point'
2643 _("State of VP in Y direction"),
2644 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2645 "toggle_vp_y",
2646 Inkscape::ICON_SIZE_DECORATION );
2647 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2648 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2649 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2650 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2651 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2652 }
2654 /* Angle Z */
2655 {
2656 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2657 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2658 eact = create_adjustment_action( "3DBoxAngleZAction",
2659 _("Angle in Z direction"), _("Angle Z:"),
2660 // Translators: PL is short for 'perspective line'
2661 _("Angle of PLs in Z direction"),
2662 "tools.shapes.3dbox", "box3d_angle_z", 30,
2663 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2664 -360.0, 360.0, 1.0, 10.0,
2665 labels, values, G_N_ELEMENTS(labels),
2666 box3d_angle_z_value_changed );
2667 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2668 g_object_set_data( holder, "box3d_angle_z_action", eact );
2669 box3d_angle_z = eact;
2670 }
2672 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2673 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2674 } else {
2675 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2676 }
2678 /* VP Z state */
2679 {
2680 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2681 // Translators: VP is short for 'vanishing point'
2682 _("State of VP in Z direction"),
2683 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2684 "toggle_vp_z",
2685 Inkscape::ICON_SIZE_DECORATION );
2686 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2687 g_object_set_data( holder, "box3d_vp_z_state_action", act );
2688 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2689 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2690 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2691 }
2693 sigc::connection *connection = new sigc::connection(
2694 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2695 );
2696 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2697 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2698 }
2700 //########################
2701 //## Spiral ##
2702 //########################
2704 static void
2705 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2706 {
2707 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2709 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2710 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2711 }
2713 // quit if run by the attr_changed listener
2714 if (g_object_get_data( tbl, "freeze" )) {
2715 return;
2716 }
2718 // in turn, prevent listener from responding
2719 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2721 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2723 bool modmade = false;
2724 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2725 items != NULL;
2726 items = items->next)
2727 {
2728 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2729 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2730 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2731 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2732 modmade = true;
2733 }
2734 }
2736 g_free(namespaced_name);
2738 if (modmade) {
2739 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2740 _("Change spiral"));
2741 }
2743 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2744 }
2746 static void
2747 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2748 {
2749 sp_spl_tb_value_changed(adj, tbl, "revolution");
2750 }
2752 static void
2753 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2754 {
2755 sp_spl_tb_value_changed(adj, tbl, "expansion");
2756 }
2758 static void
2759 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2760 {
2761 sp_spl_tb_value_changed(adj, tbl, "t0");
2762 }
2764 static void
2765 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2766 {
2767 GtkWidget *tbl = GTK_WIDGET(obj);
2769 GtkAdjustment *adj;
2771 // fixme: make settable
2772 gdouble rev = 5;
2773 gdouble exp = 1.0;
2774 gdouble t0 = 0.0;
2776 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2777 gtk_adjustment_set_value(adj, rev);
2778 gtk_adjustment_value_changed(adj);
2780 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2781 gtk_adjustment_set_value(adj, exp);
2782 gtk_adjustment_value_changed(adj);
2784 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2785 gtk_adjustment_set_value(adj, t0);
2786 gtk_adjustment_value_changed(adj);
2788 spinbutton_defocus(GTK_OBJECT(tbl));
2789 }
2792 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2793 gchar const */*old_value*/, gchar const */*new_value*/,
2794 bool /*is_interactive*/, gpointer data)
2795 {
2796 GtkWidget *tbl = GTK_WIDGET(data);
2798 // quit if run by the _changed callbacks
2799 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2800 return;
2801 }
2803 // in turn, prevent callbacks from responding
2804 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2806 GtkAdjustment *adj;
2807 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2808 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2810 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2811 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2813 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2814 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2816 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2817 }
2820 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2821 NULL, /* child_added */
2822 NULL, /* child_removed */
2823 spiral_tb_event_attr_changed,
2824 NULL, /* content_changed */
2825 NULL /* order_changed */
2826 };
2828 static void
2829 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2830 {
2831 int n_selected = 0;
2832 Inkscape::XML::Node *repr = NULL;
2834 purge_repr_listener( tbl, tbl );
2836 for (GSList const *items = selection->itemList();
2837 items != NULL;
2838 items = items->next)
2839 {
2840 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2841 n_selected++;
2842 repr = SP_OBJECT_REPR((SPItem *) items->data);
2843 }
2844 }
2846 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2848 if (n_selected == 0) {
2849 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2850 } else if (n_selected == 1) {
2851 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2853 if (repr) {
2854 g_object_set_data( tbl, "repr", repr );
2855 Inkscape::GC::anchor(repr);
2856 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2857 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2858 }
2859 } else {
2860 // FIXME: implement averaging of all parameters for multiple selected
2861 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2862 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2863 }
2864 }
2867 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2868 {
2869 EgeAdjustmentAction* eact = 0;
2871 {
2872 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2873 ege_output_action_set_use_markup( act, TRUE );
2874 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2875 g_object_set_data( holder, "mode_action", act );
2876 }
2878 /* Revolution */
2879 {
2880 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2881 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2882 eact = create_adjustment_action( "SpiralRevolutionAction",
2883 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
2884 "tools.shapes.spiral", "revolution", 3.0,
2885 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2886 0.01, 1024.0, 0.1, 1.0,
2887 labels, values, G_N_ELEMENTS(labels),
2888 sp_spl_tb_revolution_value_changed, 1, 2);
2889 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2890 }
2892 /* Expansion */
2893 {
2894 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2895 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2896 eact = create_adjustment_action( "SpiralExpansionAction",
2897 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2898 "tools.shapes.spiral", "expansion", 1.0,
2899 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2900 0.0, 1000.0, 0.01, 1.0,
2901 labels, values, G_N_ELEMENTS(labels),
2902 sp_spl_tb_expansion_value_changed);
2903 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2904 }
2906 /* T0 */
2907 {
2908 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2909 gdouble values[] = {0, 0.5, 0.9};
2910 eact = create_adjustment_action( "SpiralT0Action",
2911 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2912 "tools.shapes.spiral", "t0", 0.0,
2913 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2914 0.0, 0.999, 0.01, 1.0,
2915 labels, values, G_N_ELEMENTS(labels),
2916 sp_spl_tb_t0_value_changed);
2917 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2918 }
2920 /* Reset */
2921 {
2922 InkAction* inky = ink_action_new( "SpiralResetAction",
2923 _("Defaults"),
2924 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2925 GTK_STOCK_CLEAR,
2926 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2927 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2928 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2929 }
2932 sigc::connection *connection = new sigc::connection(
2933 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2934 );
2935 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2936 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2937 }
2939 //########################
2940 //## Pen/Pencil ##
2941 //########################
2944 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2945 {
2946 // Put stuff here
2947 }
2949 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2950 {
2951 // Put stuff here
2952 }
2954 //########################
2955 //## Tweak ##
2956 //########################
2958 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2959 {
2960 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2961 }
2963 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2964 {
2965 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2966 }
2968 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
2969 {
2970 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2971 }
2973 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2974 {
2975 int mode = ege_select_one_action_get_active( act );
2976 prefs_set_int_attribute("tools.tweak", "mode", mode);
2978 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2979 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2980 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2981 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2982 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2983 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2984 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
2985 if (doh) gtk_action_set_sensitive (doh, TRUE);
2986 if (dos) gtk_action_set_sensitive (dos, TRUE);
2987 if (dol) gtk_action_set_sensitive (dol, TRUE);
2988 if (doo) gtk_action_set_sensitive (doo, TRUE);
2989 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
2990 if (fid) gtk_action_set_sensitive (fid, FALSE);
2991 } else {
2992 if (doh) gtk_action_set_sensitive (doh, FALSE);
2993 if (dos) gtk_action_set_sensitive (dos, FALSE);
2994 if (dol) gtk_action_set_sensitive (dol, FALSE);
2995 if (doo) gtk_action_set_sensitive (doo, FALSE);
2996 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
2997 if (fid) gtk_action_set_sensitive (fid, TRUE);
2998 }
2999 }
3001 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3002 {
3003 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3004 }
3006 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3007 bool show = gtk_toggle_action_get_active( act );
3008 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3009 }
3010 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3011 bool show = gtk_toggle_action_get_active( act );
3012 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3013 }
3014 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3015 bool show = gtk_toggle_action_get_active( act );
3016 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3017 }
3018 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3019 bool show = gtk_toggle_action_get_active( act );
3020 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3021 }
3023 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3024 {
3025 {
3026 /* Width */
3027 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3028 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3029 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3030 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3031 "tools.tweak", "width", 15,
3032 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3033 1, 100, 1.0, 10.0,
3034 labels, values, G_N_ELEMENTS(labels),
3035 sp_tweak_width_value_changed, 0.01, 0, 100 );
3036 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3037 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3038 }
3041 {
3042 /* Force */
3043 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3044 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3045 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3046 _("Force"), _("Force:"), _("The force of the tweak action"),
3047 "tools.tweak", "force", 20,
3048 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3049 1, 100, 1.0, 10.0,
3050 labels, values, G_N_ELEMENTS(labels),
3051 sp_tweak_force_value_changed, 0.01, 0, 100 );
3052 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3053 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3054 }
3056 /* Mode */
3057 {
3058 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3060 GtkTreeIter iter;
3061 gtk_list_store_append( model, &iter );
3062 gtk_list_store_set( model, &iter,
3063 0, _("Push mode"),
3064 1, _("Push parts of paths in any direction"),
3065 2, "tweak_push_mode",
3066 -1 );
3068 gtk_list_store_append( model, &iter );
3069 gtk_list_store_set( model, &iter,
3070 0, _("Shrink mode"),
3071 1, _("Shrink (inset) parts of paths"),
3072 2, "tweak_shrink_mode",
3073 -1 );
3075 gtk_list_store_append( model, &iter );
3076 gtk_list_store_set( model, &iter,
3077 0, _("Grow mode"),
3078 1, _("Grow (outset) parts of paths"),
3079 2, "tweak_grow_mode",
3080 -1 );
3082 gtk_list_store_append( model, &iter );
3083 gtk_list_store_set( model, &iter,
3084 0, _("Attract mode"),
3085 1, _("Attract parts of paths towards cursor"),
3086 2, "tweak_attract_mode",
3087 -1 );
3089 gtk_list_store_append( model, &iter );
3090 gtk_list_store_set( model, &iter,
3091 0, _("Repel mode"),
3092 1, _("Repel parts of paths from cursor"),
3093 2, "tweak_repel_mode",
3094 -1 );
3096 gtk_list_store_append( model, &iter );
3097 gtk_list_store_set( model, &iter,
3098 0, _("Roughen mode"),
3099 1, _("Roughen parts of paths"),
3100 2, "tweak_roughen_mode",
3101 -1 );
3103 gtk_list_store_append( model, &iter );
3104 gtk_list_store_set( model, &iter,
3105 0, _("Color paint mode"),
3106 1, _("Paint the tool's color upon selected objects"),
3107 2, "tweak_colorpaint_mode",
3108 -1 );
3110 gtk_list_store_append( model, &iter );
3111 gtk_list_store_set( model, &iter,
3112 0, _("Color jitter mode"),
3113 1, _("Jitter the colors of selected objects"),
3114 2, "tweak_colorjitter_mode",
3115 -1 );
3117 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3118 g_object_set( act, "short_label", _("Mode:"), NULL );
3119 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3120 g_object_set_data( holder, "mode_action", act );
3122 ege_select_one_action_set_appearance( act, "full" );
3123 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3124 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3125 ege_select_one_action_set_icon_column( act, 2 );
3126 ege_select_one_action_set_tooltip_column( act, 1 );
3128 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3129 ege_select_one_action_set_active( act, mode );
3130 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3132 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3133 }
3135 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3137 {
3138 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3139 ege_output_action_set_use_markup( act, TRUE );
3140 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3141 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3142 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3143 g_object_set_data( holder, "tweak_channels_label", act);
3144 }
3146 {
3147 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3148 _("Hue"),
3149 _("In color mode, act on objects' hue"),
3150 NULL,
3151 Inkscape::ICON_SIZE_DECORATION );
3152 g_object_set( act, "short_label", _("H"), NULL );
3153 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3154 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3155 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3156 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3157 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3158 g_object_set_data( holder, "tweak_doh", act);
3159 }
3160 {
3161 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3162 _("Saturation"),
3163 _("In color mode, act on objects' saturation"),
3164 NULL,
3165 Inkscape::ICON_SIZE_DECORATION );
3166 g_object_set( act, "short_label", _("S"), NULL );
3167 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3168 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3169 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3170 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3171 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3172 g_object_set_data( holder, "tweak_dos", act );
3173 }
3174 {
3175 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3176 _("Lightness"),
3177 _("In color mode, act on objects' lightness"),
3178 NULL,
3179 Inkscape::ICON_SIZE_DECORATION );
3180 g_object_set( act, "short_label", _("L"), NULL );
3181 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3182 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3183 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3184 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3185 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3186 g_object_set_data( holder, "tweak_dol", act );
3187 }
3188 {
3189 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3190 _("Opacity"),
3191 _("In color mode, act on objects' opacity"),
3192 NULL,
3193 Inkscape::ICON_SIZE_DECORATION );
3194 g_object_set( act, "short_label", _("O"), NULL );
3195 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3196 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3197 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3198 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3199 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3200 g_object_set_data( holder, "tweak_doo", act );
3201 }
3203 { /* Fidelity */
3204 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3205 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3206 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3207 _("Fidelity"), _("Fidelity:"),
3208 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3209 "tools.tweak", "fidelity", 50,
3210 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3211 1, 100, 1.0, 10.0,
3212 labels, values, G_N_ELEMENTS(labels),
3213 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3214 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3215 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3216 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3217 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3218 g_object_set_data( holder, "tweak_fidelity", eact );
3219 }
3222 /* Use Pressure button */
3223 {
3224 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3225 _("Pressure"),
3226 _("Use the pressure of the input device to alter the force of tweak action"),
3227 "use_pressure",
3228 Inkscape::ICON_SIZE_DECORATION );
3229 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3230 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3231 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3232 }
3234 }
3237 //########################
3238 //## Calligraphy ##
3239 //########################
3241 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3242 {
3243 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3244 }
3246 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3247 {
3248 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3249 }
3251 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3252 {
3253 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3254 }
3256 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3257 {
3258 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3259 }
3261 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3262 {
3263 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3264 }
3266 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3267 {
3268 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3269 }
3271 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3272 {
3273 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3274 }
3276 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3277 {
3278 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3279 }
3281 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3282 {
3283 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3284 }
3286 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3287 {
3288 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3289 }
3291 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3292 {
3293 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3295 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3296 }
3298 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3299 {
3300 // FIXME: make defaults settable via Inkscape Options
3301 struct KeyValue {
3302 char const *key;
3303 double value;
3304 } const key_values[] = {
3305 {"mass", 0.02},
3306 {"wiggle", 0.0},
3307 {"angle", 30.0},
3308 {"width", 15},
3309 {"thinning", 0.1},
3310 {"tremor", 0.0},
3311 {"flatness", 0.9},
3312 {"cap_rounding", 0.0}
3313 };
3315 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3316 KeyValue const &kv = key_values[i];
3317 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3318 if ( adj ) {
3319 gtk_adjustment_set_value(adj, kv.value);
3320 }
3321 }
3322 }
3325 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3326 {
3327 {
3328 EgeAdjustmentAction* calligraphy_angle = 0;
3330 {
3331 /* Width */
3332 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3333 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3334 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3335 _("Pen Width"), _("Width:"),
3336 _("The width of the calligraphic pen (relative to the visible canvas area)"),
3337 "tools.calligraphic", "width", 15,
3338 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3339 1, 100, 1.0, 10.0,
3340 labels, values, G_N_ELEMENTS(labels),
3341 sp_ddc_width_value_changed, 0.01, 0, 100 );
3342 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3343 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3344 }
3346 {
3347 /* Thinning */
3348 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3349 gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3350 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3351 _("Stroke Thinning"), _("Thinning:"),
3352 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3353 "tools.calligraphic", "thinning", 0.1,
3354 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3355 -1.0, 1.0, 0.01, 0.1,
3356 labels, values, G_N_ELEMENTS(labels),
3357 sp_ddc_velthin_value_changed, 0.01, 2);
3358 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3359 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3360 }
3362 {
3363 /* Angle */
3364 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3365 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3366 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3367 _("Pen Angle"), _("Angle:"),
3368 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3369 "tools.calligraphic", "angle", 30,
3370 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3371 -90.0, 90.0, 1.0, 10.0,
3372 labels, values, G_N_ELEMENTS(labels),
3373 sp_ddc_angle_value_changed, 1, 0 );
3374 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3375 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3376 calligraphy_angle = eact;
3377 }
3379 {
3380 /* Fixation */
3381 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3382 gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3383 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3384 _("Fixation"), _("Fixation:"),
3385 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3386 "tools.calligraphic", "flatness", 0.9,
3387 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3388 0.0, 1.0, 0.01, 0.1,
3389 labels, values, G_N_ELEMENTS(labels),
3390 sp_ddc_flatness_value_changed, 0.01, 2 );
3391 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3392 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3393 }
3395 {
3396 /* Cap Rounding */
3397 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3398 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3399 // TRANSLATORS: "cap" means "end" (both start and finish) here
3400 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3401 _("Cap rounding"), _("Caps:"),
3402 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3403 "tools.calligraphic", "cap_rounding", 0.0,
3404 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3405 0.0, 5.0, 0.01, 0.1,
3406 labels, values, G_N_ELEMENTS(labels),
3407 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3408 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3409 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3410 }
3412 {
3413 /* Tremor */
3414 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3415 gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3416 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3417 _("Stroke Tremor"), _("Tremor:"),
3418 _("Increase to make strokes rugged and trembling"),
3419 "tools.calligraphic", "tremor", 0.0,
3420 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3421 0.0, 1.0, 0.01, 0.1,
3422 labels, values, G_N_ELEMENTS(labels),
3423 sp_ddc_tremor_value_changed, 0.01, 2 );
3425 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3426 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3427 }
3429 {
3430 /* Wiggle */
3431 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3432 gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3433 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3434 _("Pen Wiggle"), _("Wiggle:"),
3435 _("Increase to make the pen waver and wiggle"),
3436 "tools.calligraphic", "wiggle", 0.0,
3437 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3438 0.0, 1.0, 0.01, 0.1,
3439 labels, values, G_N_ELEMENTS(labels),
3440 sp_ddc_wiggle_value_changed, 0.01, 2 );
3441 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3442 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3443 }
3445 {
3446 /* Mass */
3447 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3448 gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3449 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3450 _("Pen Mass"), _("Mass:"),
3451 _("Increase to make the pen drag behind, as if slowed by inertia"),
3452 "tools.calligraphic", "mass", 0.02,
3453 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3454 0.0, 1.0, 0.01, 0.1,
3455 labels, values, G_N_ELEMENTS(labels),
3456 sp_ddc_mass_value_changed, 0.01, 2 );
3457 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3458 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3459 }
3462 /* Trace Background button */
3463 {
3464 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3465 _("Trace Background"),
3466 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3467 "trace_background",
3468 Inkscape::ICON_SIZE_DECORATION );
3469 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3470 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3471 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3472 }
3474 /* Use Pressure button */
3475 {
3476 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3477 _("Pressure"),
3478 _("Use the pressure of the input device to alter the width of the pen"),
3479 "use_pressure",
3480 Inkscape::ICON_SIZE_DECORATION );
3481 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3482 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3483 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3484 }
3486 /* Use Tilt button */
3487 {
3488 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3489 _("Tilt"),
3490 _("Use the tilt of the input device to alter the angle of the pen's nib"),
3491 "use_tilt",
3492 Inkscape::ICON_SIZE_DECORATION );
3493 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3494 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3495 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3496 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3497 }
3499 /* Reset */
3500 {
3501 GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3502 _("Defaults"),
3503 _("Reset all parameters to defaults"),
3504 GTK_STOCK_CLEAR );
3505 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3506 gtk_action_group_add_action( mainActions, act );
3507 gtk_action_set_sensitive( act, TRUE );
3508 }
3509 }
3510 }
3513 //########################
3514 //## Circle / Arc ##
3515 //########################
3517 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3518 {
3519 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3520 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3522 if (v1 == 0 && v2 == 0) {
3523 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3524 gtk_action_set_sensitive( ocb, FALSE );
3525 gtk_action_set_sensitive( make_whole, FALSE );
3526 }
3527 } else {
3528 gtk_action_set_sensitive( ocb, TRUE );
3529 gtk_action_set_sensitive( make_whole, TRUE );
3530 }
3531 }
3533 static void
3534 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3535 {
3536 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3538 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3539 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3540 }
3542 // quit if run by the attr_changed listener
3543 if (g_object_get_data( tbl, "freeze" )) {
3544 return;
3545 }
3547 // in turn, prevent listener from responding
3548 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3550 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3552 bool modmade = false;
3553 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3554 items != NULL;
3555 items = items->next)
3556 {
3557 SPItem *item = SP_ITEM(items->data);
3559 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3561 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3562 SPArc *arc = SP_ARC(item);
3564 if (!strcmp(value_name, "start"))
3565 ge->start = (adj->value * M_PI)/ 180;
3566 else
3567 ge->end = (adj->value * M_PI)/ 180;
3569 sp_genericellipse_normalize(ge);
3570 ((SPObject *)arc)->updateRepr();
3571 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3573 modmade = true;
3574 }
3575 }
3577 g_free(namespaced_name);
3579 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3581 sp_arctb_sensitivize( tbl, adj->value, other->value );
3583 if (modmade) {
3584 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3585 _("Arc: Change start/end"));
3586 }
3588 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3589 }
3592 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
3593 {
3594 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
3595 }
3597 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3598 {
3599 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
3600 }
3602 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3603 {
3604 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3605 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3606 if ( ege_select_one_action_get_active( act ) != 0 ) {
3607 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3608 } else {
3609 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3610 }
3611 }
3613 // quit if run by the attr_changed listener
3614 if (g_object_get_data( tbl, "freeze" )) {
3615 return;
3616 }
3618 // in turn, prevent listener from responding
3619 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3621 bool modmade = false;
3623 if ( ege_select_one_action_get_active(act) != 0 ) {
3624 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3625 items != NULL;
3626 items = items->next)
3627 {
3628 if (SP_IS_ARC((SPItem *) items->data)) {
3629 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3630 repr->setAttribute("sodipodi:open", "true");
3631 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3632 modmade = true;
3633 }
3634 }
3635 } else {
3636 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3637 items != NULL;
3638 items = items->next)
3639 {
3640 if (SP_IS_ARC((SPItem *) items->data)) {
3641 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3642 repr->setAttribute("sodipodi:open", NULL);
3643 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3644 modmade = true;
3645 }
3646 }
3647 }
3649 if (modmade) {
3650 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3651 _("Arc: Change open/closed"));
3652 }
3654 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3655 }
3657 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3658 {
3659 GtkAdjustment *adj;
3660 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3661 gtk_adjustment_set_value(adj, 0.0);
3662 gtk_adjustment_value_changed(adj);
3664 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3665 gtk_adjustment_set_value(adj, 0.0);
3666 gtk_adjustment_value_changed(adj);
3668 spinbutton_defocus( GTK_OBJECT(obj) );
3669 }
3671 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3672 gchar const */*old_value*/, gchar const */*new_value*/,
3673 bool /*is_interactive*/, gpointer data)
3674 {
3675 GObject *tbl = G_OBJECT(data);
3677 // quit if run by the _changed callbacks
3678 if (g_object_get_data( tbl, "freeze" )) {
3679 return;
3680 }
3682 // in turn, prevent callbacks from responding
3683 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3685 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3686 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3688 GtkAdjustment *adj1,*adj2;
3689 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3690 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3691 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3692 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3694 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3696 char const *openstr = NULL;
3697 openstr = repr->attribute("sodipodi:open");
3698 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3700 if (openstr) {
3701 ege_select_one_action_set_active( ocb, 1 );
3702 } else {
3703 ege_select_one_action_set_active( ocb, 0 );
3704 }
3706 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3707 }
3709 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3710 NULL, /* child_added */
3711 NULL, /* child_removed */
3712 arc_tb_event_attr_changed,
3713 NULL, /* content_changed */
3714 NULL /* order_changed */
3715 };
3718 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3719 {
3720 int n_selected = 0;
3721 Inkscape::XML::Node *repr = NULL;
3723 purge_repr_listener( tbl, tbl );
3725 for (GSList const *items = selection->itemList();
3726 items != NULL;
3727 items = items->next)
3728 {
3729 if (SP_IS_ARC((SPItem *) items->data)) {
3730 n_selected++;
3731 repr = SP_OBJECT_REPR((SPItem *) items->data);
3732 }
3733 }
3735 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3737 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3738 if (n_selected == 0) {
3739 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3740 } else if (n_selected == 1) {
3741 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3742 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3744 if (repr) {
3745 g_object_set_data( tbl, "repr", repr );
3746 Inkscape::GC::anchor(repr);
3747 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3748 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3749 }
3750 } else {
3751 // FIXME: implement averaging of all parameters for multiple selected
3752 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3753 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3754 sp_arctb_sensitivize( tbl, 1, 0 );
3755 }
3756 }
3759 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3760 {
3761 EgeAdjustmentAction* eact = 0;
3764 {
3765 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3766 ege_output_action_set_use_markup( act, TRUE );
3767 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3768 g_object_set_data( holder, "mode_action", act );
3769 }
3771 /* Start */
3772 {
3773 eact = create_adjustment_action( "ArcStartAction",
3774 _("Start"), _("Start:"),
3775 _("The angle (in degrees) from the horizontal to the arc's start point"),
3776 "tools.shapes.arc", "start", 0.0,
3777 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3778 -360.0, 360.0, 1.0, 10.0,
3779 0, 0, 0,
3780 sp_arctb_start_value_changed);
3781 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3782 }
3784 /* End */
3785 {
3786 eact = create_adjustment_action( "ArcEndAction",
3787 _("End"), _("End:"),
3788 _("The angle (in degrees) from the horizontal to the arc's end point"),
3789 "tools.shapes.arc", "end", 0.0,
3790 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3791 -360.0, 360.0, 1.0, 10.0,
3792 0, 0, 0,
3793 sp_arctb_end_value_changed);
3794 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3795 }
3797 /* Segments / Pie checkbox */
3798 {
3799 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3801 GtkTreeIter iter;
3802 gtk_list_store_append( model, &iter );
3803 gtk_list_store_set( model, &iter,
3804 0, _("Closed arc"),
3805 1, _("Switch to segment (closed shape with two radii)"),
3806 2, "circle_closed_arc",
3807 -1 );
3809 gtk_list_store_append( model, &iter );
3810 gtk_list_store_set( model, &iter,
3811 0, _("Open Arc"),
3812 1, _("Switch to arc (unclosed shape)"),
3813 2, "circle_open_arc",
3814 -1 );
3816 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3817 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3818 g_object_set_data( holder, "open_action", act );
3820 ege_select_one_action_set_appearance( act, "full" );
3821 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3822 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3823 ege_select_one_action_set_icon_column( act, 2 );
3824 ege_select_one_action_set_tooltip_column( act, 1 );
3826 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3827 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3828 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3829 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3830 }
3832 /* Make Whole */
3833 {
3834 InkAction* inky = ink_action_new( "ArcResetAction",
3835 _("Make whole"),
3836 _("Make the shape a whole ellipse, not arc or segment"),
3837 "reset_circle",
3838 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3839 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3840 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3841 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3842 g_object_set_data( holder, "make_whole", inky );
3843 }
3845 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3846 // sensitivize make whole and open checkbox
3847 {
3848 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3849 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3850 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3851 }
3854 sigc::connection *connection = new sigc::connection(
3855 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3856 );
3857 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3858 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3859 }
3864 // toggle button callbacks and updaters
3866 //########################
3867 //## Dropper ##
3868 //########################
3870 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3871 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3872 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3873 if ( set_action ) {
3874 if ( gtk_toggle_action_get_active( act ) ) {
3875 gtk_action_set_sensitive( set_action, TRUE );
3876 } else {
3877 gtk_action_set_sensitive( set_action, FALSE );
3878 }
3879 }
3881 spinbutton_defocus(GTK_OBJECT(tbl));
3882 }
3884 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3885 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3886 spinbutton_defocus(GTK_OBJECT(tbl));
3887 }
3890 /**
3891 * Dropper auxiliary toolbar construction and setup.
3892 *
3893 * TODO: Would like to add swatch of current color.
3894 * TODO: Add queue of last 5 or so colors selected with new swatches so that
3895 * can drag and drop places. Will provide a nice mixing palette.
3896 */
3897 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3898 {
3899 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3901 {
3902 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3903 _("Pick alpha"),
3904 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3905 "color_alpha_get",
3906 Inkscape::ICON_SIZE_DECORATION );
3907 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3908 g_object_set_data( holder, "pick_action", act );
3909 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3910 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3911 }
3913 {
3914 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3915 _("Set alpha"),
3916 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3917 "color_alpha_set",
3918 Inkscape::ICON_SIZE_DECORATION );
3919 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3920 g_object_set_data( holder, "set_action", act );
3921 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3922 // make sure it's disabled if we're not picking alpha
3923 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3924 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3925 }
3926 }
3929 //########################
3930 //## Text Toolbox ##
3931 //########################
3932 /*
3933 static void
3934 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3935 {
3936 //Call back for letter sizing spinbutton
3937 }
3939 static void
3940 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3941 {
3942 //Call back for line height spinbutton
3943 }
3945 static void
3946 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3947 {
3948 //Call back for horizontal kerning spinbutton
3949 }
3951 static void
3952 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3953 {
3954 //Call back for vertical kerning spinbutton
3955 }
3957 static void
3958 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3959 {
3960 //Call back for letter rotation spinbutton
3961 }*/
3963 namespace {
3965 bool visible = false;
3967 void
3968 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
3969 {
3970 SPStyle *query =
3971 sp_style_new (SP_ACTIVE_DOCUMENT);
3973 int result_fontspec =
3974 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
3976 int result_family =
3977 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3979 int result_style =
3980 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3982 int result_numbers =
3983 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3985 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3987 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3988 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3989 {
3990 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3992 if (repr)
3993 {
3994 sp_style_read_from_repr (query, repr);
3995 }
3996 else
3997 {
3998 return;
3999 }
4000 }
4002 if (query->text)
4003 {
4004 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4005 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4006 gtk_entry_set_text (GTK_ENTRY (entry), "");
4008 } else if (query->text->font_specification.value || query->text->font_family.value) {
4010 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4012 // Get the font that corresponds
4013 Glib::ustring familyName;
4015 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4016 if (font) {
4017 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4018 font->Unref();
4019 font = NULL;
4020 }
4022 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4024 Gtk::TreePath path;
4025 try {
4026 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4027 } catch (...) {
4028 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4029 return;
4030 }
4032 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4033 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4035 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4037 gtk_tree_selection_select_path (tselection, path.gobj());
4038 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4040 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4041 }
4043 //Size
4044 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4045 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4046 g_object_set_data (tbl, "size-block", gpointer(1));
4047 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4048 g_object_set_data (tbl, "size-block", gpointer(0));
4049 free (str);
4051 //Anchor
4052 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4053 {
4054 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4055 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4056 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4057 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4058 }
4059 else
4060 {
4061 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4062 {
4063 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4064 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4065 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4066 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4067 }
4068 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4069 {
4070 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4071 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4072 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4073 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4074 }
4075 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4076 {
4077 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4078 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4079 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4080 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4081 }
4082 }
4084 //Style
4085 {
4086 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4088 gboolean active = gtk_toggle_button_get_active (button);
4089 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4091 if (active != check)
4092 {
4093 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4094 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4095 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4096 }
4097 }
4099 {
4100 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4102 gboolean active = gtk_toggle_button_get_active (button);
4103 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4105 if (active != check)
4106 {
4107 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4108 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4109 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4110 }
4111 }
4113 //Orientation
4114 //locking both buttons, changing one affect all group (both)
4115 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4116 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4118 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4119 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4121 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4122 {
4123 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4124 }
4125 else
4126 {
4127 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4128 }
4129 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4130 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4131 }
4133 sp_style_unref(query);
4134 }
4136 void
4137 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4138 {
4139 sp_text_toolbox_selection_changed (selection, tbl);
4140 }
4142 void
4143 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4144 {
4145 sp_text_toolbox_selection_changed (NULL, tbl);
4146 }
4148 void
4149 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
4150 GObject *tbl)
4151 {
4152 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4153 GtkTreeModel *model = 0;
4154 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4155 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4156 GtkTreeIter iter;
4157 char *family = 0;
4159 (void)popdown;
4161 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4162 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4164 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4165 return;
4166 }
4168 gtk_tree_model_get (model, &iter, 0, &family, -1);
4170 if (g_object_get_data (G_OBJECT (selection), "block"))
4171 {
4172 gtk_entry_set_text (GTK_ENTRY (entry), family);
4173 return;
4174 }
4176 gtk_entry_set_text (GTK_ENTRY (entry), family);
4178 SPStyle *query =
4179 sp_style_new (SP_ACTIVE_DOCUMENT);
4181 int result_fontspec =
4182 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4184 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4186 SPCSSAttr *css = sp_repr_css_attr_new ();
4189 // First try to get the font spec from the stored value
4190 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4192 if (fontSpec.empty()) {
4193 // Construct a new font specification if it does not yet exist
4194 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4195 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4196 fontFromStyle->Unref();
4197 }
4199 if (!fontSpec.empty()) {
4200 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4201 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4202 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4203 if (font) {
4204 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4206 // Set all the these just in case they were altered when finding the best
4207 // match for the new family and old style...
4209 gchar c[256];
4211 font->Family(c, 256);
4212 sp_repr_css_set_property (css, "font-family", c);
4214 font->Attribute( "weight", c, 256);
4215 sp_repr_css_set_property (css, "font-weight", c);
4217 font->Attribute("style", c, 256);
4218 sp_repr_css_set_property (css, "font-style", c);
4220 font->Attribute("stretch", c, 256);
4221 sp_repr_css_set_property (css, "font-stretch", c);
4223 font->Attribute("variant", c, 256);
4224 sp_repr_css_set_property (css, "font-variant", c);
4226 font->Unref();
4227 }
4228 }
4229 }
4231 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4232 if (result_fontspec == QUERY_STYLE_NOTHING)
4233 {
4234 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4235 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4236 }
4237 else
4238 {
4239 sp_desktop_set_style (desktop, css, true, true);
4240 }
4242 sp_style_unref(query);
4244 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4245 _("Text: Change font family"));
4246 sp_repr_css_attr_unref (css);
4247 free (family);
4248 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4250 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4251 }
4253 void
4254 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
4255 GObject *tbl)
4256 {
4257 const char *family = gtk_entry_get_text (entry);
4259 try {
4260 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4261 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4262 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4263 gtk_tree_selection_select_path (selection, path.gobj());
4264 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4265 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4266 } catch (...) {
4267 if (family && strlen (family))
4268 {
4269 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4270 }
4271 }
4272 }
4274 void
4275 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
4276 gpointer data)
4277 {
4278 if (g_object_get_data (G_OBJECT (button), "block")) return;
4279 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4280 int prop = GPOINTER_TO_INT(data);
4282 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4283 SPCSSAttr *css = sp_repr_css_attr_new ();
4285 switch (prop)
4286 {
4287 case 0:
4288 {
4289 sp_repr_css_set_property (css, "text-anchor", "start");
4290 sp_repr_css_set_property (css, "text-align", "start");
4291 break;
4292 }
4293 case 1:
4294 {
4295 sp_repr_css_set_property (css, "text-anchor", "middle");
4296 sp_repr_css_set_property (css, "text-align", "center");
4297 break;
4298 }
4300 case 2:
4301 {
4302 sp_repr_css_set_property (css, "text-anchor", "end");
4303 sp_repr_css_set_property (css, "text-align", "end");
4304 break;
4305 }
4307 case 3:
4308 {
4309 sp_repr_css_set_property (css, "text-anchor", "start");
4310 sp_repr_css_set_property (css, "text-align", "justify");
4311 break;
4312 }
4313 }
4315 SPStyle *query =
4316 sp_style_new (SP_ACTIVE_DOCUMENT);
4317 int result_numbers =
4318 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4320 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4321 if (result_numbers == QUERY_STYLE_NOTHING)
4322 {
4323 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4324 }
4326 sp_style_unref(query);
4328 sp_desktop_set_style (desktop, css, true, true);
4329 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4330 _("Text: Change alignment"));
4331 sp_repr_css_attr_unref (css);
4333 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4334 }
4336 void
4337 sp_text_toolbox_style_toggled (GtkToggleButton *button,
4338 gpointer data)
4339 {
4340 if (g_object_get_data (G_OBJECT (button), "block")) return;
4342 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4343 SPCSSAttr *css = sp_repr_css_attr_new ();
4344 int prop = GPOINTER_TO_INT(data);
4345 bool active = gtk_toggle_button_get_active (button);
4347 SPStyle *query =
4348 sp_style_new (SP_ACTIVE_DOCUMENT);
4350 int result_fontspec =
4351 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4353 int result_family =
4354 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4356 int result_style =
4357 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4359 int result_numbers =
4360 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4362 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4363 Glib::ustring newFontSpec = "";
4365 if (fontSpec.empty()) {
4366 // Construct a new font specification if it does not yet exist
4367 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4368 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4369 fontFromStyle->Unref();
4370 }
4372 switch (prop)
4373 {
4374 case 0:
4375 {
4376 if (!fontSpec.empty()) {
4377 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4378 }
4379 if (fontSpec != newFontSpec) {
4380 // Don't even set the bold if the font didn't exist on the system
4381 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4382 }
4383 break;
4384 }
4386 case 1:
4387 {
4388 if (!fontSpec.empty()) {
4389 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4390 }
4391 if (fontSpec != newFontSpec) {
4392 // Don't even set the italic if the font didn't exist on the system
4393 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4394 }
4395 break;
4396 }
4397 }
4399 if (!newFontSpec.empty()) {
4400 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4401 }
4403 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4404 if (result_fontspec == QUERY_STYLE_NOTHING)
4405 {
4406 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4407 }
4409 sp_style_unref(query);
4411 sp_desktop_set_style (desktop, css, true, true);
4412 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4413 _("Text: Change font style"));
4414 sp_repr_css_attr_unref (css);
4416 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4417 }
4419 void
4420 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
4421 gpointer data)
4422 {
4423 if (g_object_get_data (G_OBJECT (button), "block")) {
4424 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4425 return;
4426 }
4428 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4429 SPCSSAttr *css = sp_repr_css_attr_new ();
4430 int prop = GPOINTER_TO_INT(data);
4432 switch (prop)
4433 {
4434 case 0:
4435 {
4436 sp_repr_css_set_property (css, "writing-mode", "lr");
4437 break;
4438 }
4440 case 1:
4441 {
4442 sp_repr_css_set_property (css, "writing-mode", "tb");
4443 break;
4444 }
4445 }
4447 SPStyle *query =
4448 sp_style_new (SP_ACTIVE_DOCUMENT);
4449 int result_numbers =
4450 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4452 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4453 if (result_numbers == QUERY_STYLE_NOTHING)
4454 {
4455 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4456 }
4458 sp_desktop_set_style (desktop, css, true, true);
4459 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4460 _("Text: Change orientation"));
4461 sp_repr_css_attr_unref (css);
4463 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4464 }
4466 gboolean
4467 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, gpointer /*data*/)
4468 {
4469 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4470 if (!desktop) return FALSE;
4472 switch (get_group0_keyval (event)) {
4473 case GDK_Escape: // defocus
4474 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4475 return TRUE; // I consumed the event
4476 break;
4477 case GDK_Return: // defocus
4478 case GDK_KP_Enter:
4479 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4480 return TRUE; // I consumed the event
4481 break;
4482 }
4483 return FALSE;
4484 }
4486 gboolean
4487 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4488 {
4489 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4490 if (!desktop) return FALSE;
4492 switch (get_group0_keyval (event)) {
4493 case GDK_Escape: // defocus
4494 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4495 sp_text_toolbox_selection_changed (NULL, tbl); // update
4496 return TRUE; // I consumed the event
4497 break;
4498 }
4499 return FALSE;
4500 }
4502 gboolean
4503 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4504 {
4505 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4506 if (!desktop) return FALSE;
4508 switch (get_group0_keyval (event)) {
4509 case GDK_KP_Enter:
4510 case GDK_Return:
4511 case GDK_Escape: // defocus
4512 gtk_widget_hide (w);
4513 visible = false;
4514 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4515 return TRUE; // I consumed the event
4516 break;
4517 }
4518 return FALSE;
4519 }
4522 void
4523 sp_text_toolbox_size_changed (GtkComboBox *cbox,
4524 GObject *tbl)
4525 {
4526 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4528 if (g_object_get_data (tbl, "size-block")) return;
4530 char *text = gtk_combo_box_get_active_text (cbox);
4532 SPCSSAttr *css = sp_repr_css_attr_new ();
4533 sp_repr_css_set_property (css, "font-size", text);
4534 free (text);
4536 SPStyle *query =
4537 sp_style_new (SP_ACTIVE_DOCUMENT);
4538 int result_numbers =
4539 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4541 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4542 if (result_numbers == QUERY_STYLE_NOTHING)
4543 {
4544 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4545 }
4547 sp_style_unref(query);
4549 sp_desktop_set_style (desktop, css, true, true);
4550 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4551 _("Text: Change font size"));
4552 sp_repr_css_attr_unref (css);
4555 if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
4556 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4557 }
4559 void
4560 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
4561 GObject *tbl)
4562 {
4563 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4564 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4565 int x, y;
4567 if (!visible)
4568 {
4569 gdk_window_get_origin (widget->window, &x, &y);
4570 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4571 gtk_widget_show_all (popdown);
4573 gdk_pointer_grab (widget->window, TRUE,
4574 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4575 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4576 GDK_POINTER_MOTION_MASK),
4577 NULL, NULL, GDK_CURRENT_TIME);
4579 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4581 visible = true;
4582 }
4583 else
4584 {
4585 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4586 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4587 gtk_widget_hide (popdown);
4588 visible = false;
4589 }
4590 }
4592 gboolean
4593 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
4594 GdkEventFocus */*event*/,
4595 GObject */*tbl*/)
4596 {
4597 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4598 return FALSE;
4599 }
4601 gboolean
4602 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
4603 GdkEventFocus */*event*/,
4604 GObject */*tbl*/)
4605 {
4606 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4608 gtk_widget_hide (popdown);
4609 visible = false;
4610 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4611 return TRUE;
4612 }
4614 void
4615 cell_data_func (GtkTreeViewColumn */*column*/,
4616 GtkCellRenderer *cell,
4617 GtkTreeModel *tree_model,
4618 GtkTreeIter *iter,
4619 gpointer /*data*/)
4620 {
4621 char *family,
4622 *family_escaped,
4623 *sample_escaped;
4625 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4627 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4629 family_escaped = g_markup_escape_text (family, -1);
4630 sample_escaped = g_markup_escape_text (sample, -1);
4632 std::stringstream markup;
4633 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4634 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4636 free (family);
4637 free (family_escaped);
4638 free (sample_escaped);
4639 }
4641 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4642 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4643 if (completion) {
4644 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4645 g_object_unref (completion);
4646 }
4647 }
4649 GtkWidget*
4650 sp_text_toolbox_new (SPDesktop *desktop)
4651 {
4652 GtkWidget *tbl = gtk_hbox_new (FALSE, 0);
4654 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4655 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4657 GtkTooltips *tt = gtk_tooltips_new();
4658 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4660 ////////////Family
4661 //Window
4662 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4663 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4665 //Entry
4666 GtkWidget *entry = gtk_entry_new ();
4667 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4668 GtkEntryCompletion *completion = gtk_entry_completion_new ();
4669 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4670 gtk_entry_completion_set_text_column (completion, 0);
4671 gtk_entry_completion_set_minimum_key_length (completion, 1);
4672 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4673 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4674 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4675 aux_toolbox_space (tbl, 1);
4676 gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4677 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4679 //Button
4680 GtkWidget *button = gtk_button_new ();
4681 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4682 gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4684 //Popdown
4685 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
4686 GtkWidget *treeview = gtk_tree_view_new ();
4688 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
4689 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
4690 gtk_tree_view_column_pack_start (column, cell, FALSE);
4691 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4692 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4693 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4695 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4696 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4697 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4699 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4701 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4702 gtk_container_add (GTK_CONTAINER (sw), treeview);
4704 gtk_container_add (GTK_CONTAINER (window), sw);
4705 gtk_widget_set_size_request (window, 300, 450);
4707 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4708 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4709 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4711 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4713 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4714 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4716 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4717 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4719 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4720 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4721 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4722 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4723 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4725 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4726 aux_toolbox_space (tbl, 1);
4727 GtkWidget *box = gtk_event_box_new ();
4728 gtk_container_add (GTK_CONTAINER (box), image);
4729 gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4730 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4731 GtkTooltips *tooltips = gtk_tooltips_new ();
4732 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4733 gtk_widget_hide (GTK_WIDGET (box));
4734 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4736 ////////////Size
4737 const char *sizes[] = {
4738 "4", "6", "8", "9", "10", "11", "12", "13", "14",
4739 "16", "18", "20", "22", "24", "28",
4740 "32", "36", "40", "48", "56", "64", "72", "144"
4741 };
4743 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4744 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4745 gtk_widget_set_size_request (cbox, 80, -1);
4746 aux_toolbox_space (tbl, 1);
4747 gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4748 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4749 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4750 gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
4752 //spacer
4753 aux_toolbox_space (tbl, 4);
4754 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4756 ////////////Text anchor
4757 GtkWidget *group = gtk_radio_button_new (NULL);
4758 GtkWidget *row = gtk_hbox_new (FALSE, 4);
4759 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4761 // left
4762 GtkWidget *rbutton = group;
4763 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4764 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4765 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4767 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4768 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
4769 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4770 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4772 // center
4773 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4774 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4775 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4776 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4778 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4779 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
4780 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4781 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4783 // right
4784 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4785 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4786 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4787 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4789 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4790 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
4791 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4792 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4794 // fill
4795 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4796 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4797 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4798 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4800 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4801 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
4802 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4803 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4805 aux_toolbox_space (tbl, 1);
4806 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4808 //spacer
4809 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4811 ////////////Text style
4812 row = gtk_hbox_new (FALSE, 4);
4814 // bold
4815 rbutton = gtk_toggle_button_new ();
4816 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4817 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4818 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4819 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4821 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4822 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
4823 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4825 // italic
4826 rbutton = gtk_toggle_button_new ();
4827 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4828 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4829 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4830 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4832 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4833 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
4834 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4836 aux_toolbox_space (tbl, 1);
4837 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4839 //spacer
4840 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4842 ////////////Text orientation
4843 group = gtk_radio_button_new (NULL);
4844 row = gtk_hbox_new (FALSE, 4);
4845 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4847 // horizontal
4848 rbutton = group;
4849 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4850 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4851 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4852 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4854 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4855 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4856 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4858 // vertical
4859 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4860 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4861 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4862 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4863 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4865 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4866 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
4867 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4868 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4871 //watch selection
4872 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4874 sigc::connection *c_selection_changed =
4875 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4876 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4877 pool->add_connection ("selection-changed", c_selection_changed);
4879 sigc::connection *c_selection_modified =
4880 new sigc::connection (sp_desktop_selection (desktop)->connectModified
4881 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4882 pool->add_connection ("selection-modified", c_selection_modified);
4884 sigc::connection *c_subselection_changed =
4885 new sigc::connection (desktop->connectToolSubselectionChanged
4886 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4887 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4889 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4892 gtk_widget_show_all (tbl);
4893 return tbl;
4895 } // end of sp_text_toolbox_new()
4897 }//<unnamed> namespace
4900 //#########################
4901 //## Connector ##
4902 //#########################
4904 static void sp_connector_path_set_avoid(void)
4905 {
4906 cc_selection_set_avoid(true);
4907 }
4910 static void sp_connector_path_set_ignore(void)
4911 {
4912 cc_selection_set_avoid(false);
4913 }
4917 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4918 {
4919 // quit if run by the _changed callbacks
4920 if (g_object_get_data( tbl, "freeze" )) {
4921 return;
4922 }
4924 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4925 SPDocument *doc = sp_desktop_document(desktop);
4927 if (!sp_document_get_undo_sensitive(doc))
4928 {
4929 return;
4930 }
4932 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4934 if ( repr->attribute("inkscape:connector-spacing") ) {
4935 gdouble priorValue = gtk_adjustment_get_value(adj);
4936 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4937 if ( priorValue == gtk_adjustment_get_value(adj) ) {
4938 return;
4939 }
4940 } else if ( adj->value == defaultConnSpacing ) {
4941 return;
4942 }
4944 // in turn, prevent callbacks from responding
4945 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4947 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4948 SP_OBJECT(desktop->namedview)->updateRepr();
4950 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4951 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4952 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4953 NR::Matrix m = NR::identity();
4954 avoid_item_move(&m, item);
4955 }
4957 if (items) {
4958 g_slist_free(items);
4959 }
4961 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4962 _("Change connector spacing"));
4964 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4966 spinbutton_defocus(GTK_OBJECT(tbl));
4967 }
4969 static void sp_connector_graph_layout(void)
4970 {
4971 if (!SP_ACTIVE_DESKTOP) return;
4973 // hack for clones, see comment in align-and-distribute.cpp
4974 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4975 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4977 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
4979 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
4981 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
4982 }
4984 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4985 {
4986 if ( gtk_toggle_action_get_active( act ) ) {
4987 prefs_set_string_attribute("tools.connector", "directedlayout",
4988 "true");
4989 } else {
4990 prefs_set_string_attribute("tools.connector", "directedlayout",
4991 "false");
4992 }
4993 }
4995 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4996 {
4997 if ( gtk_toggle_action_get_active( act ) ) {
4998 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4999 "true");
5000 } else {
5001 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5002 "false");
5003 }
5004 }
5007 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5008 {
5009 prefs_set_double_attribute("tools.connector", "length", adj->value);
5010 spinbutton_defocus(GTK_OBJECT(tbl));
5011 }
5013 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5014 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5015 bool /*is_interactive*/, gpointer data)
5016 {
5017 GtkWidget *tbl = GTK_WIDGET(data);
5019 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5020 return;
5021 }
5022 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5023 return;
5024 }
5026 GtkAdjustment *adj = (GtkAdjustment*)
5027 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5028 gdouble spacing = defaultConnSpacing;
5029 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5031 gtk_adjustment_set_value(adj, spacing);
5032 }
5035 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5036 NULL, /* child_added */
5037 NULL, /* child_removed */
5038 connector_tb_event_attr_changed,
5039 NULL, /* content_changed */
5040 NULL /* order_changed */
5041 };
5044 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5045 {
5046 {
5047 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5048 _("Avoid"),
5049 _("Make connectors avoid selected objects"),
5050 "connector_avoid",
5051 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5052 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5053 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5054 }
5056 {
5057 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5058 _("Ignore"),
5059 _("Make connectors ignore selected objects"),
5060 "connector_ignore",
5061 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5062 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5063 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5064 }
5066 EgeAdjustmentAction* eact = 0;
5068 // Spacing spinbox
5069 eact = create_adjustment_action( "ConnectorSpacingAction",
5070 _("Connector Spacing"), _("Spacing:"),
5071 _("The amount of space left around objects by auto-routing connectors"),
5072 "tools.connector", "spacing", defaultConnSpacing,
5073 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5074 0, 100, 1.0, 10.0,
5075 0, 0, 0,
5076 connector_spacing_changed, 1, 0 );
5077 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5079 // Graph (connector network) layout
5080 {
5081 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5082 _("Graph"),
5083 _("Nicely arrange selected connector network"),
5084 "graph_layout",
5085 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5086 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5087 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5088 }
5090 // Default connector length spinbox
5091 eact = create_adjustment_action( "ConnectorLengthAction",
5092 _("Connector Length"), _("Length:"),
5093 _("Ideal length for connectors when layout is applied"),
5094 "tools.connector", "length", 100,
5095 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5096 10, 1000, 10.0, 100.0,
5097 0, 0, 0,
5098 connector_length_changed, 1, 0 );
5099 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5102 // Directed edges toggle button
5103 {
5104 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5105 _("Downwards"),
5106 _("Make connectors with end-markers (arrows) point downwards"),
5107 "directed_graph",
5108 Inkscape::ICON_SIZE_DECORATION );
5109 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5111 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5112 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5113 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5115 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5116 }
5118 // Avoid overlaps toggle button
5119 {
5120 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5121 _("Remove overlaps"),
5122 _("Do not allow overlapping shapes"),
5123 "remove_overlaps",
5124 Inkscape::ICON_SIZE_DECORATION );
5125 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5127 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5128 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5129 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5131 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5132 }
5134 // Code to watch for changes to the connector-spacing attribute in
5135 // the XML.
5136 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5137 g_assert(repr != NULL);
5139 purge_repr_listener( holder, holder );
5141 if (repr) {
5142 g_object_set_data( holder, "repr", repr );
5143 Inkscape::GC::anchor(repr);
5144 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5145 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5146 }
5147 } // end of sp_connector_toolbox_prep()
5150 //#########################
5151 //## Paintbucket ##
5152 //#########################
5154 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5155 {
5156 gint channels = ege_select_one_action_get_active( act );
5157 flood_channels_set_channels( channels );
5158 }
5160 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5161 {
5162 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5163 }
5165 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5166 {
5167 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5168 }
5170 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5171 {
5172 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5173 SPUnit const *unit = tracker->getActiveUnit();
5175 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5177 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5178 }
5180 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5181 {
5182 // FIXME: make defaults settable via Inkscape Options
5183 struct KeyValue {
5184 char const *key;
5185 double value;
5186 } const key_values[] = {
5187 {"threshold", 15},
5188 {"offset", 0.0}
5189 };
5191 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5192 KeyValue const &kv = key_values[i];
5193 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5194 if ( adj ) {
5195 gtk_adjustment_set_value(adj, kv.value);
5196 }
5197 }
5199 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5200 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5201 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5202 ege_select_one_action_set_active( autogap_action, 0 );
5203 }
5205 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5206 {
5207 EgeAdjustmentAction* eact = 0;
5209 {
5210 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5212 GList* items = 0;
5213 gint count = 0;
5214 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5215 {
5216 GtkTreeIter iter;
5217 gtk_list_store_append( model, &iter );
5218 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5219 count++;
5220 }
5221 g_list_free( items );
5222 items = 0;
5223 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5224 g_object_set( act1, "short_label", _("Fill by:"), NULL );
5225 ege_select_one_action_set_appearance( act1, "compact" );
5226 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5227 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5228 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5229 g_object_set_data( holder, "channels_action", act1 );
5230 }
5232 // Spacing spinbox
5233 {
5234 eact = create_adjustment_action(
5235 "ThresholdAction",
5236 _("Fill Threshold"), _("Threshold:"),
5237 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5238 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5239 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5240 0, 0, 0,
5241 paintbucket_threshold_changed, 1, 0 );
5243 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5244 }
5246 // Create the units menu.
5247 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5248 tracker->setActiveUnit(sp_unit_get_by_abbreviation(prefs_get_string_attribute("tools.paintbucket", "offsetunits")));
5249 g_object_set_data( holder, "tracker", tracker );
5250 {
5251 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5252 gtk_action_group_add_action( mainActions, act );
5253 }
5255 // Offset spinbox
5256 {
5257 eact = create_adjustment_action(
5258 "OffsetAction",
5259 _("Grow/shrink by"), _("Grow/shrink by:"),
5260 _("The amount to grow (positive) or shrink (negative) the created fill path"),
5261 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5262 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5263 0, 0, 0,
5264 paintbucket_offset_changed, 1, 2);
5265 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5267 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5268 }
5270 /* Auto Gap */
5271 {
5272 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5274 GList* items = 0;
5275 gint count = 0;
5276 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5277 {
5278 GtkTreeIter iter;
5279 gtk_list_store_append( model, &iter );
5280 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5281 count++;
5282 }
5283 g_list_free( items );
5284 items = 0;
5285 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5286 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5287 ege_select_one_action_set_appearance( act2, "compact" );
5288 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5289 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5290 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5291 g_object_set_data( holder, "autogap_action", act2 );
5292 }
5294 /* Reset */
5295 {
5296 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5297 _("Defaults"),
5298 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5299 GTK_STOCK_CLEAR );
5300 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5301 gtk_action_group_add_action( mainActions, act );
5302 gtk_action_set_sensitive( act, TRUE );
5303 }
5305 }
5307 /*
5308 Local Variables:
5309 mode:c++
5310 c-file-style:"stroustrup"
5311 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5312 indent-tabs-mode:nil
5313 fill-column:99
5314 End:
5315 */
5316 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :