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::INFINITE : Proj::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 SPCSSAttr *css = sp_repr_css_attr_new ();
4186 std::string fontSpec = query->text->font_specification.value;
4187 if (!fontSpec.empty()) {
4188 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4189 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4190 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4191 if (font) {
4192 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4194 // Set all the these just in case they were altered when finding the best
4195 // match for the new family and old style...
4197 gchar c[256];
4199 font->Family(c, 256);
4200 sp_repr_css_set_property (css, "font-family", c);
4202 font->Attribute( "weight", c, 256);
4203 sp_repr_css_set_property (css, "font-weight", c);
4205 font->Attribute("style", c, 256);
4206 sp_repr_css_set_property (css, "font-style", c);
4208 font->Attribute("stretch", c, 256);
4209 sp_repr_css_set_property (css, "font-stretch", c);
4211 font->Attribute("variant", c, 256);
4212 sp_repr_css_set_property (css, "font-variant", c);
4214 font->Unref();
4215 }
4216 }
4217 }
4219 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4220 if (result_fontspec == QUERY_STYLE_NOTHING)
4221 {
4222 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4223 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4224 }
4225 else
4226 {
4227 sp_desktop_set_style (desktop, css, true, true);
4228 }
4230 sp_style_unref(query);
4232 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4233 _("Text: Change font family"));
4234 sp_repr_css_attr_unref (css);
4235 free (family);
4236 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4238 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4239 }
4241 void
4242 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
4243 GObject *tbl)
4244 {
4245 const char *family = gtk_entry_get_text (entry);
4247 try {
4248 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4249 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4250 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4251 gtk_tree_selection_select_path (selection, path.gobj());
4252 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4253 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4254 } catch (...) {
4255 if (family && strlen (family))
4256 {
4257 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4258 }
4259 }
4260 }
4262 void
4263 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
4264 gpointer data)
4265 {
4266 if (g_object_get_data (G_OBJECT (button), "block")) return;
4267 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4268 int prop = GPOINTER_TO_INT(data);
4270 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4271 SPCSSAttr *css = sp_repr_css_attr_new ();
4273 switch (prop)
4274 {
4275 case 0:
4276 {
4277 sp_repr_css_set_property (css, "text-anchor", "start");
4278 sp_repr_css_set_property (css, "text-align", "start");
4279 break;
4280 }
4281 case 1:
4282 {
4283 sp_repr_css_set_property (css, "text-anchor", "middle");
4284 sp_repr_css_set_property (css, "text-align", "center");
4285 break;
4286 }
4288 case 2:
4289 {
4290 sp_repr_css_set_property (css, "text-anchor", "end");
4291 sp_repr_css_set_property (css, "text-align", "end");
4292 break;
4293 }
4295 case 3:
4296 {
4297 sp_repr_css_set_property (css, "text-anchor", "start");
4298 sp_repr_css_set_property (css, "text-align", "justify");
4299 break;
4300 }
4301 }
4303 SPStyle *query =
4304 sp_style_new (SP_ACTIVE_DOCUMENT);
4305 int result_numbers =
4306 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4308 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4309 if (result_numbers == QUERY_STYLE_NOTHING)
4310 {
4311 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4312 }
4314 sp_style_unref(query);
4316 sp_desktop_set_style (desktop, css, true, true);
4317 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4318 _("Text: Change alignment"));
4319 sp_repr_css_attr_unref (css);
4321 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4322 }
4324 void
4325 sp_text_toolbox_style_toggled (GtkToggleButton *button,
4326 gpointer data)
4327 {
4328 if (g_object_get_data (G_OBJECT (button), "block")) return;
4330 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4331 SPCSSAttr *css = sp_repr_css_attr_new ();
4332 int prop = GPOINTER_TO_INT(data);
4333 bool active = gtk_toggle_button_get_active (button);
4335 SPStyle *query =
4336 sp_style_new (SP_ACTIVE_DOCUMENT);
4337 int result_fontspec =
4338 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4340 Glib::ustring fontSpec = query->text->font_specification.value;
4341 Glib::ustring newFontSpec;
4343 switch (prop)
4344 {
4345 case 0:
4346 {
4347 if (!fontSpec.empty()) {
4348 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4349 }
4350 if (fontSpec != newFontSpec) {
4351 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4352 }
4353 break;
4354 }
4356 case 1:
4357 {
4358 if (!fontSpec.empty()) {
4359 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4360 }
4361 if (fontSpec != newFontSpec) {
4362 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4363 }
4364 break;
4365 }
4366 }
4368 if (!fontSpec.empty()) {
4369 sp_repr_css_set_property (css, "-inkscape-font-specification", fontSpec.c_str());
4370 }
4372 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4373 if (result_fontspec == QUERY_STYLE_NOTHING)
4374 {
4375 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4376 }
4378 sp_style_unref(query);
4380 sp_desktop_set_style (desktop, css, true, true);
4381 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4382 _("Text: Change font style"));
4383 sp_repr_css_attr_unref (css);
4385 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4386 }
4388 void
4389 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
4390 gpointer data)
4391 {
4392 if (g_object_get_data (G_OBJECT (button), "block")) {
4393 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4394 return;
4395 }
4397 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4398 SPCSSAttr *css = sp_repr_css_attr_new ();
4399 int prop = GPOINTER_TO_INT(data);
4401 switch (prop)
4402 {
4403 case 0:
4404 {
4405 sp_repr_css_set_property (css, "writing-mode", "lr");
4406 break;
4407 }
4409 case 1:
4410 {
4411 sp_repr_css_set_property (css, "writing-mode", "tb");
4412 break;
4413 }
4414 }
4416 SPStyle *query =
4417 sp_style_new (SP_ACTIVE_DOCUMENT);
4418 int result_numbers =
4419 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4421 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4422 if (result_numbers == QUERY_STYLE_NOTHING)
4423 {
4424 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4425 }
4427 sp_desktop_set_style (desktop, css, true, true);
4428 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4429 _("Text: Change orientation"));
4430 sp_repr_css_attr_unref (css);
4432 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4433 }
4435 gboolean
4436 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, gpointer /*data*/)
4437 {
4438 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4439 if (!desktop) return FALSE;
4441 switch (get_group0_keyval (event)) {
4442 case GDK_Escape: // defocus
4443 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4444 return TRUE; // I consumed the event
4445 break;
4446 case GDK_Return: // defocus
4447 case GDK_KP_Enter:
4448 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4449 return TRUE; // I consumed the event
4450 break;
4451 }
4452 return FALSE;
4453 }
4455 gboolean
4456 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4457 {
4458 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4459 if (!desktop) return FALSE;
4461 switch (get_group0_keyval (event)) {
4462 case GDK_Escape: // defocus
4463 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4464 sp_text_toolbox_selection_changed (NULL, tbl); // update
4465 return TRUE; // I consumed the event
4466 break;
4467 }
4468 return FALSE;
4469 }
4471 gboolean
4472 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4473 {
4474 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4475 if (!desktop) return FALSE;
4477 switch (get_group0_keyval (event)) {
4478 case GDK_KP_Enter:
4479 case GDK_Return:
4480 case GDK_Escape: // defocus
4481 gtk_widget_hide (w);
4482 visible = false;
4483 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4484 return TRUE; // I consumed the event
4485 break;
4486 }
4487 return FALSE;
4488 }
4491 void
4492 sp_text_toolbox_size_changed (GtkComboBox *cbox,
4493 GObject *tbl)
4494 {
4495 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4497 if (g_object_get_data (tbl, "size-block")) return;
4499 char *text = gtk_combo_box_get_active_text (cbox);
4501 SPCSSAttr *css = sp_repr_css_attr_new ();
4502 sp_repr_css_set_property (css, "font-size", text);
4503 free (text);
4505 SPStyle *query =
4506 sp_style_new (SP_ACTIVE_DOCUMENT);
4507 int result_numbers =
4508 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4510 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4511 if (result_numbers == QUERY_STYLE_NOTHING)
4512 {
4513 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4514 }
4516 sp_style_unref(query);
4518 sp_desktop_set_style (desktop, css, true, true);
4519 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4520 _("Text: Change font size"));
4521 sp_repr_css_attr_unref (css);
4524 if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
4525 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4526 }
4528 void
4529 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
4530 GObject *tbl)
4531 {
4532 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4533 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4534 int x, y;
4536 if (!visible)
4537 {
4538 gdk_window_get_origin (widget->window, &x, &y);
4539 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4540 gtk_widget_show_all (popdown);
4542 gdk_pointer_grab (widget->window, TRUE,
4543 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4544 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4545 GDK_POINTER_MOTION_MASK),
4546 NULL, NULL, GDK_CURRENT_TIME);
4548 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4550 visible = true;
4551 }
4552 else
4553 {
4554 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4555 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4556 gtk_widget_hide (popdown);
4557 visible = false;
4558 }
4559 }
4561 gboolean
4562 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
4563 GdkEventFocus */*event*/,
4564 GObject */*tbl*/)
4565 {
4566 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4567 return FALSE;
4568 }
4570 gboolean
4571 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
4572 GdkEventFocus */*event*/,
4573 GObject */*tbl*/)
4574 {
4575 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4577 gtk_widget_hide (popdown);
4578 visible = false;
4579 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4580 return TRUE;
4581 }
4583 void
4584 cell_data_func (GtkTreeViewColumn */*column*/,
4585 GtkCellRenderer *cell,
4586 GtkTreeModel *tree_model,
4587 GtkTreeIter *iter,
4588 gpointer /*data*/)
4589 {
4590 char *family,
4591 *family_escaped,
4592 *sample_escaped;
4594 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4596 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4598 family_escaped = g_markup_escape_text (family, -1);
4599 sample_escaped = g_markup_escape_text (sample, -1);
4601 std::stringstream markup;
4602 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4603 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4605 free (family);
4606 free (family_escaped);
4607 free (sample_escaped);
4608 }
4610 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4611 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4612 if (completion) {
4613 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4614 g_object_unref (completion);
4615 }
4616 }
4618 GtkWidget*
4619 sp_text_toolbox_new (SPDesktop *desktop)
4620 {
4621 GtkWidget *tbl = gtk_hbox_new (FALSE, 0);
4623 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4624 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4626 GtkTooltips *tt = gtk_tooltips_new();
4627 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4629 ////////////Family
4630 //Window
4631 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4632 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4634 //Entry
4635 GtkWidget *entry = gtk_entry_new ();
4636 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4637 GtkEntryCompletion *completion = gtk_entry_completion_new ();
4638 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4639 gtk_entry_completion_set_text_column (completion, 0);
4640 gtk_entry_completion_set_minimum_key_length (completion, 1);
4641 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4642 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4643 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4644 aux_toolbox_space (tbl, 1);
4645 gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4646 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4648 //Button
4649 GtkWidget *button = gtk_button_new ();
4650 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4651 gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4653 //Popdown
4654 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
4655 GtkWidget *treeview = gtk_tree_view_new ();
4657 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
4658 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
4659 gtk_tree_view_column_pack_start (column, cell, FALSE);
4660 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4661 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4662 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4664 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4665 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4666 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4668 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4670 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4671 gtk_container_add (GTK_CONTAINER (sw), treeview);
4673 gtk_container_add (GTK_CONTAINER (window), sw);
4674 gtk_widget_set_size_request (window, 300, 450);
4676 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4677 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4678 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4680 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4682 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4683 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4685 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4686 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4688 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4689 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4690 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4691 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4692 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4694 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4695 aux_toolbox_space (tbl, 1);
4696 GtkWidget *box = gtk_event_box_new ();
4697 gtk_container_add (GTK_CONTAINER (box), image);
4698 gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4699 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4700 GtkTooltips *tooltips = gtk_tooltips_new ();
4701 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4702 gtk_widget_hide (GTK_WIDGET (box));
4703 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4705 ////////////Size
4706 const char *sizes[] = {
4707 "4", "6", "8", "9", "10", "11", "12", "13", "14",
4708 "16", "18", "20", "22", "24", "28",
4709 "32", "36", "40", "48", "56", "64", "72", "144"
4710 };
4712 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4713 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4714 gtk_widget_set_size_request (cbox, 80, -1);
4715 aux_toolbox_space (tbl, 1);
4716 gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4717 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4718 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4719 gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
4721 //spacer
4722 aux_toolbox_space (tbl, 4);
4723 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4725 ////////////Text anchor
4726 GtkWidget *group = gtk_radio_button_new (NULL);
4727 GtkWidget *row = gtk_hbox_new (FALSE, 4);
4728 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4730 // left
4731 GtkWidget *rbutton = group;
4732 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4733 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4734 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4736 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4737 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
4738 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4739 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4741 // center
4742 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4743 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4744 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4745 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4747 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4748 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
4749 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4750 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4752 // right
4753 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4754 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4755 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4756 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4758 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4759 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
4760 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4761 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4763 // fill
4764 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4765 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4766 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4767 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4769 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4770 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
4771 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4772 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4774 aux_toolbox_space (tbl, 1);
4775 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4777 //spacer
4778 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4780 ////////////Text style
4781 row = gtk_hbox_new (FALSE, 4);
4783 // bold
4784 rbutton = gtk_toggle_button_new ();
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_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4787 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4788 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4790 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4791 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
4792 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4794 // italic
4795 rbutton = gtk_toggle_button_new ();
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_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4798 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4799 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4801 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4802 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
4803 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
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 orientation
4812 group = gtk_radio_button_new (NULL);
4813 row = gtk_hbox_new (FALSE, 4);
4814 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4816 // horizontal
4817 rbutton = group;
4818 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4819 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4820 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4821 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4823 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4824 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4825 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4827 // vertical
4828 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4829 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4830 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4831 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4832 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4834 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4835 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
4836 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4837 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4840 //watch selection
4841 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4843 sigc::connection *c_selection_changed =
4844 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4845 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4846 pool->add_connection ("selection-changed", c_selection_changed);
4848 sigc::connection *c_selection_modified =
4849 new sigc::connection (sp_desktop_selection (desktop)->connectModified
4850 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4851 pool->add_connection ("selection-modified", c_selection_modified);
4853 sigc::connection *c_subselection_changed =
4854 new sigc::connection (desktop->connectToolSubselectionChanged
4855 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4856 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4858 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4861 gtk_widget_show_all (tbl);
4862 return tbl;
4864 } // end of sp_text_toolbox_new()
4866 }//<unnamed> namespace
4869 //#########################
4870 //## Connector ##
4871 //#########################
4873 static void sp_connector_path_set_avoid(void)
4874 {
4875 cc_selection_set_avoid(true);
4876 }
4879 static void sp_connector_path_set_ignore(void)
4880 {
4881 cc_selection_set_avoid(false);
4882 }
4886 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4887 {
4888 // quit if run by the _changed callbacks
4889 if (g_object_get_data( tbl, "freeze" )) {
4890 return;
4891 }
4893 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4894 SPDocument *doc = sp_desktop_document(desktop);
4896 if (!sp_document_get_undo_sensitive(doc))
4897 {
4898 return;
4899 }
4901 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4903 if ( repr->attribute("inkscape:connector-spacing") ) {
4904 gdouble priorValue = gtk_adjustment_get_value(adj);
4905 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4906 if ( priorValue == gtk_adjustment_get_value(adj) ) {
4907 return;
4908 }
4909 } else if ( adj->value == defaultConnSpacing ) {
4910 return;
4911 }
4913 // in turn, prevent callbacks from responding
4914 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4916 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4917 SP_OBJECT(desktop->namedview)->updateRepr();
4919 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4920 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4921 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4922 NR::Matrix m = NR::identity();
4923 avoid_item_move(&m, item);
4924 }
4926 if (items) {
4927 g_slist_free(items);
4928 }
4930 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4931 _("Change connector spacing"));
4933 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4935 spinbutton_defocus(GTK_OBJECT(tbl));
4936 }
4938 static void sp_connector_graph_layout(void)
4939 {
4940 if (!SP_ACTIVE_DESKTOP) return;
4942 // hack for clones, see comment in align-and-distribute.cpp
4943 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4944 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4946 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
4948 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
4950 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
4951 }
4953 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4954 {
4955 if ( gtk_toggle_action_get_active( act ) ) {
4956 prefs_set_string_attribute("tools.connector", "directedlayout",
4957 "true");
4958 } else {
4959 prefs_set_string_attribute("tools.connector", "directedlayout",
4960 "false");
4961 }
4962 }
4964 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4965 {
4966 if ( gtk_toggle_action_get_active( act ) ) {
4967 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4968 "true");
4969 } else {
4970 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4971 "false");
4972 }
4973 }
4976 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
4977 {
4978 prefs_set_double_attribute("tools.connector", "length", adj->value);
4979 spinbutton_defocus(GTK_OBJECT(tbl));
4980 }
4982 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
4983 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
4984 bool /*is_interactive*/, gpointer data)
4985 {
4986 GtkWidget *tbl = GTK_WIDGET(data);
4988 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
4989 return;
4990 }
4991 if (strcmp(name, "inkscape:connector-spacing") != 0) {
4992 return;
4993 }
4995 GtkAdjustment *adj = (GtkAdjustment*)
4996 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
4997 gdouble spacing = defaultConnSpacing;
4998 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5000 gtk_adjustment_set_value(adj, spacing);
5001 }
5004 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5005 NULL, /* child_added */
5006 NULL, /* child_removed */
5007 connector_tb_event_attr_changed,
5008 NULL, /* content_changed */
5009 NULL /* order_changed */
5010 };
5013 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5014 {
5015 {
5016 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5017 _("Avoid"),
5018 _("Make connectors avoid selected objects"),
5019 "connector_avoid",
5020 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5021 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5022 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5023 }
5025 {
5026 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5027 _("Ignore"),
5028 _("Make connectors ignore selected objects"),
5029 "connector_ignore",
5030 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5031 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5032 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5033 }
5035 EgeAdjustmentAction* eact = 0;
5037 // Spacing spinbox
5038 eact = create_adjustment_action( "ConnectorSpacingAction",
5039 _("Connector Spacing"), _("Spacing:"),
5040 _("The amount of space left around objects by auto-routing connectors"),
5041 "tools.connector", "spacing", defaultConnSpacing,
5042 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5043 0, 100, 1.0, 10.0,
5044 0, 0, 0,
5045 connector_spacing_changed, 1, 0 );
5046 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5048 // Graph (connector network) layout
5049 {
5050 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5051 _("Graph"),
5052 _("Nicely arrange selected connector network"),
5053 "graph_layout",
5054 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5055 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5056 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5057 }
5059 // Default connector length spinbox
5060 eact = create_adjustment_action( "ConnectorLengthAction",
5061 _("Connector Length"), _("Length:"),
5062 _("Ideal length for connectors when layout is applied"),
5063 "tools.connector", "length", 100,
5064 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5065 10, 1000, 10.0, 100.0,
5066 0, 0, 0,
5067 connector_length_changed, 1, 0 );
5068 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5071 // Directed edges toggle button
5072 {
5073 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5074 _("Downwards"),
5075 _("Make connectors with end-markers (arrows) point downwards"),
5076 "directed_graph",
5077 Inkscape::ICON_SIZE_DECORATION );
5078 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5080 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5081 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5082 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5084 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5085 }
5087 // Avoid overlaps toggle button
5088 {
5089 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5090 _("Remove overlaps"),
5091 _("Do not allow overlapping shapes"),
5092 "remove_overlaps",
5093 Inkscape::ICON_SIZE_DECORATION );
5094 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5096 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5097 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5098 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5100 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5101 }
5103 // Code to watch for changes to the connector-spacing attribute in
5104 // the XML.
5105 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5106 g_assert(repr != NULL);
5108 purge_repr_listener( holder, holder );
5110 if (repr) {
5111 g_object_set_data( holder, "repr", repr );
5112 Inkscape::GC::anchor(repr);
5113 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5114 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5115 }
5116 } // end of sp_connector_toolbox_prep()
5119 //#########################
5120 //## Paintbucket ##
5121 //#########################
5123 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5124 {
5125 gint channels = ege_select_one_action_get_active( act );
5126 flood_channels_set_channels( channels );
5127 }
5129 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5130 {
5131 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5132 }
5134 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5135 {
5136 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5137 }
5139 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5140 {
5141 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5142 SPUnit const *unit = tracker->getActiveUnit();
5144 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5146 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5147 }
5149 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5150 {
5151 // FIXME: make defaults settable via Inkscape Options
5152 struct KeyValue {
5153 char const *key;
5154 double value;
5155 } const key_values[] = {
5156 {"threshold", 15},
5157 {"offset", 0.0}
5158 };
5160 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5161 KeyValue const &kv = key_values[i];
5162 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5163 if ( adj ) {
5164 gtk_adjustment_set_value(adj, kv.value);
5165 }
5166 }
5168 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5169 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5170 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5171 ege_select_one_action_set_active( autogap_action, 0 );
5172 }
5174 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5175 {
5176 EgeAdjustmentAction* eact = 0;
5178 {
5179 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5181 GList* items = 0;
5182 gint count = 0;
5183 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5184 {
5185 GtkTreeIter iter;
5186 gtk_list_store_append( model, &iter );
5187 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5188 count++;
5189 }
5190 g_list_free( items );
5191 items = 0;
5192 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5193 g_object_set( act1, "short_label", _("Fill by:"), NULL );
5194 ege_select_one_action_set_appearance( act1, "compact" );
5195 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5196 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5197 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5198 g_object_set_data( holder, "channels_action", act1 );
5199 }
5201 // Spacing spinbox
5202 {
5203 eact = create_adjustment_action(
5204 "ThresholdAction",
5205 _("Fill Threshold"), _("Threshold:"),
5206 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5207 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5208 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5209 0, 0, 0,
5210 paintbucket_threshold_changed, 1, 0 );
5212 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5213 }
5215 // Create the units menu.
5216 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5217 tracker->setActiveUnit(sp_unit_get_by_abbreviation(prefs_get_string_attribute("tools.paintbucket", "offsetunits")));
5218 g_object_set_data( holder, "tracker", tracker );
5219 {
5220 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5221 gtk_action_group_add_action( mainActions, act );
5222 }
5224 // Offset spinbox
5225 {
5226 eact = create_adjustment_action(
5227 "OffsetAction",
5228 _("Grow/shrink by"), _("Grow/shrink by:"),
5229 _("The amount to grow (positive) or shrink (negative) the created fill path"),
5230 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5231 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5232 0, 0, 0,
5233 paintbucket_offset_changed, 1, 2);
5234 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5236 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5237 }
5239 /* Auto Gap */
5240 {
5241 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5243 GList* items = 0;
5244 gint count = 0;
5245 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5246 {
5247 GtkTreeIter iter;
5248 gtk_list_store_append( model, &iter );
5249 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5250 count++;
5251 }
5252 g_list_free( items );
5253 items = 0;
5254 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5255 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5256 ege_select_one_action_set_appearance( act2, "compact" );
5257 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5258 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5259 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5260 g_object_set_data( holder, "autogap_action", act2 );
5261 }
5263 /* Reset */
5264 {
5265 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5266 _("Defaults"),
5267 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5268 GTK_STOCK_CLEAR );
5269 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5270 gtk_action_group_add_action( mainActions, act );
5271 gtk_action_set_sensitive( act, TRUE );
5272 }
5274 }
5276 /*
5277 Local Variables:
5278 mode:c++
5279 c-file-style:"stroustrup"
5280 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5281 indent-tabs-mode:nil
5282 fill-column:99
5283 End:
5284 */
5285 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :