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 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
635 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
637 gtk_widget_set_sensitive(tb, FALSE);
639 GtkWidget *hb = gtk_handle_box_new();
640 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
641 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
642 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
644 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
645 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
647 gtk_container_add(GTK_CONTAINER(hb), tb);
648 gtk_widget_show(GTK_WIDGET(tb));
650 sigc::connection* conn = new sigc::connection;
651 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
653 return hb;
654 }
656 //####################################
657 //# Commands Bar
658 //####################################
660 GtkWidget *
661 sp_commands_toolbox_new()
662 {
663 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
665 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
667 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
668 gtk_widget_set_sensitive(tb, FALSE);
670 GtkWidget *hb = gtk_handle_box_new();
671 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
672 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
673 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
675 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
676 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
678 gtk_container_add(GTK_CONTAINER(hb), tb);
679 gtk_widget_show(GTK_WIDGET(tb));
681 sigc::connection* conn = new sigc::connection;
682 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
684 return hb;
685 }
687 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
688 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
689 gchar const *path, gchar const *data, gdouble def,
690 GtkWidget *focusTarget,
691 GtkWidget *us,
692 GObject *dataKludge,
693 gboolean altx, gchar const *altx_mark,
694 gdouble lower, gdouble upper, gdouble step, gdouble page,
695 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
696 void (*callback)(GtkAdjustment *, GObject *),
697 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
698 {
699 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
700 lower, upper, step, page, page ) );
701 if (us) {
702 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
703 }
705 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
707 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
708 if ( shortLabel ) {
709 g_object_set( act, "short_label", shortLabel, NULL );
710 }
712 if ( (descrCount > 0) && descrLabels && descrValues ) {
713 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
714 }
716 if ( focusTarget ) {
717 ege_adjustment_action_set_focuswidget( act, focusTarget );
718 }
720 if ( altx && altx_mark ) {
721 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
722 }
724 if ( dataKludge ) {
725 g_object_set_data( dataKludge, data, adj );
726 }
728 // Using a cast just to make sure we pass in the right kind of function pointer
729 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
731 return act;
732 }
735 //####################################
736 //# node editing callbacks
737 //####################################
739 /**
740 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
741 */
742 static ShapeEditor *get_current_shape_editor()
743 {
744 if (!SP_ACTIVE_DESKTOP) {
745 return NULL;
746 }
748 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
750 if (!SP_IS_NODE_CONTEXT(event_context)) {
751 return NULL;
752 }
754 return SP_NODE_CONTEXT(event_context)->shape_editor;
755 }
758 void
759 sp_node_path_edit_add(void)
760 {
761 ShapeEditor *shape_editor = get_current_shape_editor();
762 if (shape_editor) shape_editor->add_node();
763 }
765 void
766 sp_node_path_edit_delete(void)
767 {
768 ShapeEditor *shape_editor = get_current_shape_editor();
769 if (shape_editor) shape_editor->delete_nodes();
770 }
772 void
773 sp_node_path_edit_delete_segment(void)
774 {
775 ShapeEditor *shape_editor = get_current_shape_editor();
776 if (shape_editor) shape_editor->delete_segment();
777 }
779 void
780 sp_node_path_edit_break(void)
781 {
782 ShapeEditor *shape_editor = get_current_shape_editor();
783 if (shape_editor) shape_editor->break_at_nodes();
784 }
786 void
787 sp_node_path_edit_join(void)
788 {
789 ShapeEditor *shape_editor = get_current_shape_editor();
790 if (shape_editor) shape_editor->join_nodes();
791 }
793 void
794 sp_node_path_edit_join_segment(void)
795 {
796 ShapeEditor *shape_editor = get_current_shape_editor();
797 if (shape_editor) shape_editor->join_segments();
798 }
800 void
801 sp_node_path_edit_toline(void)
802 {
803 ShapeEditor *shape_editor = get_current_shape_editor();
804 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
805 }
807 void
808 sp_node_path_edit_tocurve(void)
809 {
810 ShapeEditor *shape_editor = get_current_shape_editor();
811 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
812 }
814 void
815 sp_node_path_edit_cusp(void)
816 {
817 ShapeEditor *shape_editor = get_current_shape_editor();
818 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
819 }
821 void
822 sp_node_path_edit_smooth(void)
823 {
824 ShapeEditor *shape_editor = get_current_shape_editor();
825 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
826 }
828 void
829 sp_node_path_edit_symmetrical(void)
830 {
831 ShapeEditor *shape_editor = get_current_shape_editor();
832 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
833 }
835 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
836 bool show = gtk_toggle_action_get_active( act );
837 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
838 ShapeEditor *shape_editor = get_current_shape_editor();
839 if (shape_editor) shape_editor->show_handles(show);
840 }
842 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
843 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
844 }
846 /* is called when the node selection is modified */
847 static void
848 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
849 {
850 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
851 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
852 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
853 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
855 // quit if run by the attr_changed listener
856 if (g_object_get_data( tbl, "freeze" )) {
857 return;
858 }
860 // in turn, prevent listener from responding
861 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
863 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
864 SPUnit const *unit = tracker->getActiveUnit();
866 ShapeEditor *shape_editor = get_current_shape_editor();
867 if (shape_editor && shape_editor->has_nodepath()) {
868 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
869 int n_selected = 0;
870 if (nodepath) {
871 n_selected = nodepath->numSelected();
872 }
874 if (n_selected == 0) {
875 gtk_action_set_sensitive(xact, FALSE);
876 gtk_action_set_sensitive(yact, FALSE);
877 } else {
878 gtk_action_set_sensitive(xact, TRUE);
879 gtk_action_set_sensitive(yact, TRUE);
880 NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
881 NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
883 if (n_selected == 1) {
884 NR::Point sel_node = nodepath->singleSelectedCoords();
885 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
886 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
887 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
888 }
889 } else {
890 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
891 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
892 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
893 /* Note: Currently x and y will always have a value, even if the coordinates of the
894 selected nodes don't coincide (in this case we use the coordinates of the center
895 of the bounding box). So the entries are never set to zero. */
896 // FIXME: Maybe we should clear the entry if several nodes are selected
897 // instead of providing a kind of average value
898 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
899 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
900 }
901 }
902 }
903 } else {
904 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
905 gtk_action_set_sensitive(xact, FALSE);
906 gtk_action_set_sensitive(yact, FALSE);
907 }
909 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
910 }
912 static void
913 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
914 {
915 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
917 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
918 SPUnit const *unit = tracker->getActiveUnit();
920 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
921 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
922 }
924 // quit if run by the attr_changed listener
925 if (g_object_get_data( tbl, "freeze" )) {
926 return;
927 }
929 // in turn, prevent listener from responding
930 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
932 ShapeEditor *shape_editor = get_current_shape_editor();
933 if (shape_editor && shape_editor->has_nodepath()) {
934 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
935 if (!strcmp(value_name, "x")) {
936 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
937 }
938 if (!strcmp(value_name, "y")) {
939 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
940 }
941 }
943 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
944 }
946 static void
947 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
948 {
949 sp_node_path_value_changed(adj, tbl, "x");
950 }
952 static void
953 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
954 {
955 sp_node_path_value_changed(adj, tbl, "y");
956 }
958 //################################
959 //## Node Editing Toolbox ##
960 //################################
962 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
963 {
964 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
965 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
966 g_object_set_data( holder, "tracker", tracker );
968 {
969 InkAction* inky = ink_action_new( "NodeInsertAction",
970 _("Insert node"),
971 _("Insert new nodes into selected segments"),
972 "node_insert",
973 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
974 g_object_set( inky, "short_label", _("Insert"), NULL );
975 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
976 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
977 }
979 {
980 InkAction* inky = ink_action_new( "NodeDeleteAction",
981 _("Delete node"),
982 _("Delete selected nodes"),
983 "node_delete",
984 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
985 g_object_set( inky, "short_label", _("Delete"), NULL );
986 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
987 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
988 }
990 {
991 InkAction* inky = ink_action_new( "NodeJoinAction",
992 _("Join endnodes"),
993 _("Join selected endnodes"),
994 "node_join",
995 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
996 g_object_set( inky, "short_label", _("Join"), NULL );
997 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
998 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
999 }
1001 {
1002 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1003 _("Join Segment"),
1004 _("Join selected endnodes with a new segment"),
1005 "node_join_segment",
1006 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1007 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1008 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1009 }
1011 {
1012 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1013 _("Delete Segment"),
1014 _("Split path between two non-endpoint nodes"),
1015 "node_delete_segment",
1016 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1017 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1018 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1019 }
1021 {
1022 InkAction* inky = ink_action_new( "NodeBreakAction",
1023 _("Node Break"),
1024 _("Break path at selected nodes"),
1025 "node_break",
1026 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1027 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1028 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1029 }
1031 {
1032 InkAction* inky = ink_action_new( "NodeCuspAction",
1033 _("Node Cusp"),
1034 _("Make selected nodes corner"),
1035 "node_cusp",
1036 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1037 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1038 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1039 }
1041 {
1042 InkAction* inky = ink_action_new( "NodeSmoothAction",
1043 _("Node Smooth"),
1044 _("Make selected nodes smooth"),
1045 "node_smooth",
1046 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1047 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1048 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1049 }
1051 {
1052 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1053 _("Node Symmetric"),
1054 _("Make selected nodes symmetric"),
1055 "node_symmetric",
1056 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1057 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1058 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1059 }
1061 {
1062 InkAction* inky = ink_action_new( "NodeLineAction",
1063 _("Node Line"),
1064 _("Make selected segments lines"),
1065 "node_line",
1066 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1067 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1068 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1069 }
1071 {
1072 InkAction* inky = ink_action_new( "NodeCurveAction",
1073 _("Node Curve"),
1074 _("Make selected segments curves"),
1075 "node_curve",
1076 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1077 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1078 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1079 }
1081 {
1082 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1083 _("Show Handles"),
1084 _("Show the Bezier handles of selected nodes"),
1085 "nodes_show_handles",
1086 Inkscape::ICON_SIZE_DECORATION );
1087 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1088 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1089 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1090 }
1092 {
1093 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1094 _("Next Path Effect Parameter"),
1095 _("Show next Path Effect parameter for editing"),
1096 "edit_next_parameter",
1097 Inkscape::ICON_SIZE_DECORATION );
1098 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1099 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1100 }
1102 /* X coord of selected node(s) */
1103 {
1104 EgeAdjustmentAction* eact = 0;
1105 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1106 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1107 eact = create_adjustment_action( "NodeXAction",
1108 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1109 "tools.nodes", "Xcoord", 0,
1110 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1111 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1112 labels, values, G_N_ELEMENTS(labels),
1113 sp_node_path_x_value_changed );
1114 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1115 g_object_set_data( holder, "nodes_x_action", eact );
1116 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1117 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1118 }
1120 /* Y coord of selected node(s) */
1121 {
1122 EgeAdjustmentAction* eact = 0;
1123 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1124 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1125 eact = create_adjustment_action( "NodeYAction",
1126 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1127 "tools.nodes", "Ycoord", 0,
1128 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1129 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1130 labels, values, G_N_ELEMENTS(labels),
1131 sp_node_path_y_value_changed );
1132 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1133 g_object_set_data( holder, "nodes_y_action", eact );
1134 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1135 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1136 }
1138 // add the units menu
1139 {
1140 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1141 gtk_action_group_add_action( mainActions, act );
1142 }
1144 sigc::connection *connection = new sigc::connection (
1145 desktop->connectToolSubselectionChanged(sigc::bind (sigc::ptr_fun(sp_node_toolbox_coord_changed), (GObject *)holder))
1146 );
1148 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1149 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1150 } // end of sp_node_toolbox_prep()
1153 //########################
1154 //## Zoom Toolbox ##
1155 //########################
1157 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1158 {
1159 // no custom GtkAction setup needed
1160 } // end of sp_zoom_toolbox_prep()
1162 void
1163 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1164 {
1165 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")));
1166 }
1169 void
1170 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1171 {
1172 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")));
1173 }
1175 void
1176 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1177 {
1178 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")));
1179 }
1181 static void
1182 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1183 {
1184 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1185 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1187 if (old_desktop) {
1188 GList *children, *iter;
1190 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1191 for ( iter = children ; iter ; iter = iter->next ) {
1192 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1193 }
1194 g_list_free(children);
1195 }
1197 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1199 if (desktop) {
1200 gtk_widget_set_sensitive(toolbox, TRUE);
1201 setup_func(toolbox, desktop);
1202 update_func(desktop, desktop->event_context, toolbox);
1203 *conn = desktop->connectEventContextChanged
1204 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1205 } else {
1206 gtk_widget_set_sensitive(toolbox, FALSE);
1207 }
1209 } // end of toolbox_set_desktop()
1212 static void
1213 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1214 {
1215 GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1216 gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1217 if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1218 // "toolbox.tools" was not set. Fallback to older value
1219 shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1221 // Copy the setting forwards
1222 prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1223 }
1224 Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1226 for (int i = 0 ; tools[i].type_name ; i++ ) {
1227 GtkWidget *button =
1228 sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1229 SP_BUTTON_TYPE_TOGGLE,
1230 Inkscape::Verb::get(tools[i].verb),
1231 Inkscape::Verb::get(tools[i].doubleclick_verb),
1232 desktop,
1233 tooltips );
1235 g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1236 (gpointer)button );
1237 }
1238 }
1241 static void
1242 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1243 {
1244 gchar const *const tname = ( eventcontext
1245 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1246 : NULL );
1247 for (int i = 0 ; tools[i].type_name ; i++ ) {
1248 SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1249 sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1250 }
1251 }
1253 static void
1254 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1255 {
1256 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1257 GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1258 GtkUIManager* mgr = gtk_ui_manager_new();
1259 GError* errVal = 0;
1260 gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1261 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1263 std::map<std::string, GtkWidget*> dataHolders;
1265 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1266 if ( aux_toolboxes[i].prep_func ) {
1267 // converted to GtkActions and UIManager
1269 GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1270 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1271 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1272 dataHolders[aux_toolboxes[i].type_name] = kludge;
1273 aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1274 } else {
1276 GtkWidget *sub_toolbox = 0;
1277 if (aux_toolboxes[i].create_func == NULL)
1278 sub_toolbox = sp_empty_toolbox_new(desktop);
1279 else {
1280 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1281 }
1283 gtk_size_group_add_widget( grouper, sub_toolbox );
1285 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1286 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1288 }
1289 }
1291 // Second pass to create toolbars *after* all GtkActions are created
1292 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1293 if ( aux_toolboxes[i].prep_func ) {
1294 // converted to GtkActions and UIManager
1296 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1298 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1299 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1301 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1302 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1303 g_free( tmp );
1304 tmp = 0;
1306 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1307 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1308 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1309 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1310 }
1311 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1314 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1316 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1317 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1318 swatch->setDesktop( desktop );
1319 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1320 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1321 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1322 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 );
1323 }
1325 gtk_widget_show_all( holder );
1326 sp_set_font_size_smaller( holder );
1328 gtk_size_group_add_widget( grouper, holder );
1330 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1331 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1332 }
1333 }
1335 g_object_unref( G_OBJECT(grouper) );
1336 }
1338 static void
1339 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1340 {
1341 gchar const *tname = ( eventcontext
1342 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1343 : NULL );
1344 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1345 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1346 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1347 gtk_widget_show_all(sub_toolbox);
1348 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1349 } else {
1350 gtk_widget_hide(sub_toolbox);
1351 }
1352 }
1353 }
1355 static void
1356 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1357 {
1358 gchar const * descr =
1359 "<ui>"
1360 " <toolbar name='CommandsToolbar'>"
1361 " <toolitem action='FileNew' />"
1362 " <toolitem action='FileOpen' />"
1363 " <toolitem action='FileSave' />"
1364 " <toolitem action='FilePrint' />"
1365 " <separator />"
1366 " <toolitem action='FileImport' />"
1367 " <toolitem action='FileExport' />"
1368 " <separator />"
1369 " <toolitem action='EditUndo' />"
1370 " <toolitem action='EditRedo' />"
1371 " <separator />"
1372 " <toolitem action='EditCopy' />"
1373 " <toolitem action='EditCut' />"
1374 " <toolitem action='EditPaste' />"
1375 " <separator />"
1376 " <toolitem action='ZoomSelection' />"
1377 " <toolitem action='ZoomDrawing' />"
1378 " <toolitem action='ZoomPage' />"
1379 " <separator />"
1380 " <toolitem action='EditDuplicate' />"
1381 " <toolitem action='EditClone' />"
1382 " <toolitem action='EditUnlinkClone' />"
1383 " <separator />"
1384 " <toolitem action='SelectionGroup' />"
1385 " <toolitem action='SelectionUnGroup' />"
1386 " <separator />"
1387 " <toolitem action='DialogFillStroke' />"
1388 " <toolitem action='DialogText' />"
1389 " <toolitem action='DialogXMLEditor' />"
1390 " <toolitem action='DialogAlignDistribute' />"
1391 " <separator />"
1392 " <toolitem action='DialogPreferences' />"
1393 " <toolitem action='DialogDocumentProperties' />"
1394 " </toolbar>"
1395 "</ui>";
1396 GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1399 GtkUIManager* mgr = gtk_ui_manager_new();
1400 GError* errVal = 0;
1402 gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1403 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1405 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1406 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1407 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1408 }
1409 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1410 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1411 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1414 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1415 }
1417 static void
1418 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1419 {
1420 }
1422 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1423 {
1424 gtk_widget_show(toolbox_toplevel);
1425 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1427 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1428 if (!shown_toolbox) {
1429 return;
1430 }
1431 gtk_widget_show(toolbox);
1433 gtk_widget_show_all(shown_toolbox);
1434 }
1436 void
1437 aux_toolbox_space(GtkWidget *tb, gint space)
1438 {
1439 gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1440 }
1442 static GtkWidget *
1443 sp_empty_toolbox_new(SPDesktop *desktop)
1444 {
1445 GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1446 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1447 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1449 gtk_widget_show_all(tbl);
1450 sp_set_font_size_smaller (tbl);
1452 return tbl;
1453 }
1455 // helper UI functions
1457 GtkWidget *
1458 sp_tb_spinbutton(
1459 gchar *label, gchar const *tooltip,
1460 gchar const *path, gchar const *data, gdouble def,
1461 GtkWidget *us,
1462 GtkWidget *tbl,
1463 gboolean altx, gchar const *altx_mark,
1464 gdouble lower, gdouble upper, gdouble step, gdouble page,
1465 void (*callback)(GtkAdjustment *, GtkWidget *),
1466 gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1467 {
1468 GtkTooltips *tt = gtk_tooltips_new();
1470 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1472 GtkWidget *l = gtk_label_new(label);
1473 gtk_widget_show(l);
1474 gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1475 gtk_container_add(GTK_CONTAINER(hb), l);
1477 GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1478 lower, upper, step, page, page);
1479 gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1480 if (us)
1481 sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1483 GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1484 gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1485 if (altx)
1486 gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1487 gtk_widget_set_size_request(sb,
1488 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1489 AUX_SPINBUTTON_HEIGHT);
1490 gtk_widget_show(sb);
1491 gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1492 gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1493 gtk_container_add(GTK_CONTAINER(hb), sb);
1494 gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1496 return hb;
1497 }
1499 #define MODE_LABEL_WIDTH 70
1501 //########################
1502 //## Star ##
1503 //########################
1505 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1506 {
1507 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1509 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1510 // do not remember prefs if this call is initiated by an undo change, because undoing object
1511 // creation sets bogus values to its attributes before it is deleted
1512 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1513 }
1515 // quit if run by the attr_changed listener
1516 if (g_object_get_data( dataKludge, "freeze" )) {
1517 return;
1518 }
1520 // in turn, prevent listener from responding
1521 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1523 bool modmade = false;
1525 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1526 GSList const *items = selection->itemList();
1527 for (; items != NULL; items = items->next) {
1528 if (SP_IS_STAR((SPItem *) items->data)) {
1529 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1530 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1531 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1532 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1533 + M_PI / (gint)adj->value));
1534 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1535 modmade = true;
1536 }
1537 }
1538 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1539 _("Star: Change number of corners"));
1541 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1542 }
1544 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1545 {
1546 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1548 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1549 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1550 }
1552 // quit if run by the attr_changed listener
1553 if (g_object_get_data( dataKludge, "freeze" )) {
1554 return;
1555 }
1557 // in turn, prevent listener from responding
1558 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1560 bool modmade = false;
1561 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1562 GSList const *items = selection->itemList();
1563 for (; items != NULL; items = items->next) {
1564 if (SP_IS_STAR((SPItem *) items->data)) {
1565 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1567 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1568 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1569 if (r2 < r1) {
1570 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1571 } else {
1572 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1573 }
1575 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1576 modmade = true;
1577 }
1578 }
1580 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1581 _("Star: Change spoke ratio"));
1583 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1584 }
1586 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1587 {
1588 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1589 bool flat = ege_select_one_action_get_active( act ) == 0;
1591 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1592 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1593 flat ? "true" : "false" );
1594 }
1596 // quit if run by the attr_changed listener
1597 if (g_object_get_data( dataKludge, "freeze" )) {
1598 return;
1599 }
1601 // in turn, prevent listener from responding
1602 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1604 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1605 GSList const *items = selection->itemList();
1606 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1607 bool modmade = false;
1609 if ( prop_action ) {
1610 gtk_action_set_sensitive( prop_action, !flat );
1611 }
1613 for (; items != NULL; items = items->next) {
1614 if (SP_IS_STAR((SPItem *) items->data)) {
1615 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1616 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1617 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1618 modmade = true;
1619 }
1620 }
1622 if (modmade) {
1623 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1624 flat ? _("Make polygon") : _("Make star"));
1625 }
1627 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1628 }
1630 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1631 {
1632 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1634 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1635 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1636 }
1638 // quit if run by the attr_changed listener
1639 if (g_object_get_data( dataKludge, "freeze" )) {
1640 return;
1641 }
1643 // in turn, prevent listener from responding
1644 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1646 bool modmade = false;
1648 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1649 GSList const *items = selection->itemList();
1650 for (; items != NULL; items = items->next) {
1651 if (SP_IS_STAR((SPItem *) items->data)) {
1652 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1653 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1654 SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1655 modmade = true;
1656 }
1657 }
1658 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1659 _("Star: Change rounding"));
1661 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1662 }
1664 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1665 {
1666 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1668 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1669 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1670 }
1672 // quit if run by the attr_changed listener
1673 if (g_object_get_data( dataKludge, "freeze" )) {
1674 return;
1675 }
1677 // in turn, prevent listener from responding
1678 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1680 bool modmade = false;
1682 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1683 GSList const *items = selection->itemList();
1684 for (; items != NULL; items = items->next) {
1685 if (SP_IS_STAR((SPItem *) items->data)) {
1686 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1687 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1688 SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1689 modmade = true;
1690 }
1691 }
1692 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1693 _("Star: Change randomization"));
1695 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1696 }
1699 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1700 gchar const */*old_value*/, gchar const */*new_value*/,
1701 bool /*is_interactive*/, gpointer data)
1702 {
1703 GtkWidget *tbl = GTK_WIDGET(data);
1705 // quit if run by the _changed callbacks
1706 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1707 return;
1708 }
1710 // in turn, prevent callbacks from responding
1711 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1713 GtkAdjustment *adj = 0;
1715 if (!strcmp(name, "inkscape:randomized")) {
1716 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1717 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1718 } else if (!strcmp(name, "inkscape:rounded")) {
1719 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1720 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1721 } else if (!strcmp(name, "inkscape:flatsided")) {
1722 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1723 char const *flatsides = repr->attribute("inkscape:flatsided");
1724 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1725 if ( flatsides && !strcmp(flatsides,"false") ) {
1726 ege_select_one_action_set_active( flat_action, 1 );
1727 gtk_action_set_sensitive( prop_action, TRUE );
1728 } else {
1729 ege_select_one_action_set_active( flat_action, 0 );
1730 gtk_action_set_sensitive( prop_action, FALSE );
1731 }
1732 } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
1733 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1734 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1735 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1736 if (r2 < r1) {
1737 gtk_adjustment_set_value(adj, r2/r1);
1738 } else {
1739 gtk_adjustment_set_value(adj, r1/r2);
1740 }
1741 } else if (!strcmp(name, "sodipodi:sides")) {
1742 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1743 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1744 }
1746 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1747 }
1750 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1751 {
1752 NULL, /* child_added */
1753 NULL, /* child_removed */
1754 star_tb_event_attr_changed,
1755 NULL, /* content_changed */
1756 NULL /* order_changed */
1757 };
1760 /**
1761 * \param selection Should not be NULL.
1762 */
1763 static void
1764 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1765 {
1766 int n_selected = 0;
1767 Inkscape::XML::Node *repr = NULL;
1769 purge_repr_listener( tbl, tbl );
1771 for (GSList const *items = selection->itemList();
1772 items != NULL;
1773 items = items->next)
1774 {
1775 if (SP_IS_STAR((SPItem *) items->data)) {
1776 n_selected++;
1777 repr = SP_OBJECT_REPR((SPItem *) items->data);
1778 }
1779 }
1781 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1783 if (n_selected == 0) {
1784 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1785 } else if (n_selected == 1) {
1786 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1788 if (repr) {
1789 g_object_set_data( tbl, "repr", repr );
1790 Inkscape::GC::anchor(repr);
1791 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1792 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1793 }
1794 } else {
1795 // FIXME: implement averaging of all parameters for multiple selected stars
1796 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1797 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1798 }
1799 }
1802 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1803 {
1804 // FIXME: in this and all other _default functions, set some flag telling the value_changed
1805 // callbacks to lump all the changes for all selected objects in one undo step
1807 GtkAdjustment *adj = 0;
1809 // fixme: make settable in prefs!
1810 gint mag = 5;
1811 gdouble prop = 0.5;
1812 gboolean flat = FALSE;
1813 gdouble randomized = 0;
1814 gdouble rounded = 0;
1816 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1817 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1819 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1820 gtk_action_set_sensitive( sb2, !flat );
1822 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1823 gtk_adjustment_set_value(adj, mag);
1824 gtk_adjustment_value_changed(adj);
1826 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1827 gtk_adjustment_set_value(adj, prop);
1828 gtk_adjustment_value_changed(adj);
1830 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1831 gtk_adjustment_set_value(adj, rounded);
1832 gtk_adjustment_value_changed(adj);
1834 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1835 gtk_adjustment_set_value(adj, randomized);
1836 gtk_adjustment_value_changed(adj);
1837 }
1840 void
1841 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1842 {
1843 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1844 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1845 GtkWidget *l = gtk_label_new(NULL);
1846 gtk_label_set_markup(GTK_LABEL(l), title);
1847 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1848 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1849 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1850 }
1853 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1854 {
1855 {
1856 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1857 ege_output_action_set_use_markup( act, TRUE );
1858 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1859 g_object_set_data( holder, "mode_action", act );
1860 }
1862 {
1863 EgeAdjustmentAction* eact = 0;
1864 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1865 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1867 /* Flatsided checkbox */
1868 {
1869 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1871 GtkTreeIter iter;
1872 gtk_list_store_append( model, &iter );
1873 gtk_list_store_set( model, &iter,
1874 0, _("Polygon"),
1875 1, _("Regular polygon (with one handle) instead of a star"),
1876 2, "star_flat",
1877 -1 );
1879 gtk_list_store_append( model, &iter );
1880 gtk_list_store_set( model, &iter,
1881 0, _("Star"),
1882 1, _("Star instead of a regular polygon (with one handle)"),
1883 2, "star_angled",
1884 -1 );
1886 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
1887 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1888 g_object_set_data( holder, "flat_action", act );
1890 ege_select_one_action_set_appearance( act, "full" );
1891 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1892 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1893 ege_select_one_action_set_icon_column( act, 2 );
1894 ege_select_one_action_set_tooltip_column( act, 1 );
1896 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1897 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1898 }
1900 /* Magnitude */
1901 {
1902 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1903 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1904 eact = create_adjustment_action( "MagnitudeAction",
1905 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
1906 "tools.shapes.star", "magnitude", 3,
1907 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1908 3, 1024, 1, 5,
1909 labels, values, G_N_ELEMENTS(labels),
1910 sp_stb_magnitude_value_changed,
1911 1.0, 0 );
1912 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1913 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1914 }
1916 /* Spoke ratio */
1917 {
1918 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1919 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1920 eact = create_adjustment_action( "SpokeAction",
1921 _("Spoke ratio"), _("Spoke ratio:"),
1922 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1923 // Base radius is the same for the closest handle.
1924 _("Base radius to tip radius ratio"),
1925 "tools.shapes.star", "proportion", 0.5,
1926 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1927 0.01, 1.0, 0.01, 0.1,
1928 labels, values, G_N_ELEMENTS(labels),
1929 sp_stb_proportion_value_changed );
1930 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1931 g_object_set_data( holder, "prop_action", eact );
1932 }
1934 if ( !isFlatSided ) {
1935 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1936 } else {
1937 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1938 }
1940 /* Roundedness */
1941 {
1942 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1943 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1944 eact = create_adjustment_action( "RoundednessAction",
1945 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1946 "tools.shapes.star", "rounded", 0.0,
1947 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1948 -10.0, 10.0, 0.01, 0.1,
1949 labels, values, G_N_ELEMENTS(labels),
1950 sp_stb_rounded_value_changed );
1951 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1952 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1953 }
1955 /* Randomization */
1956 {
1957 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1958 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1959 eact = create_adjustment_action( "RandomizationAction",
1960 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
1961 "tools.shapes.star", "randomized", 0.0,
1962 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1963 -10.0, 10.0, 0.001, 0.01,
1964 labels, values, G_N_ELEMENTS(labels),
1965 sp_stb_randomized_value_changed, 0.1, 3 );
1966 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1967 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1968 }
1969 }
1971 {
1972 /* Reset */
1973 {
1974 GtkAction* act = gtk_action_new( "StarResetAction",
1975 _("Defaults"),
1976 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1977 GTK_STOCK_CLEAR );
1978 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1979 gtk_action_group_add_action( mainActions, act );
1980 gtk_action_set_sensitive( act, TRUE );
1981 }
1982 }
1984 sigc::connection *connection = new sigc::connection(
1985 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
1986 );
1987 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1988 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1989 }
1992 //########################
1993 //## Rect ##
1994 //########################
1996 static void sp_rtb_sensitivize( GObject *tbl )
1997 {
1998 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
1999 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2000 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2002 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2003 gtk_action_set_sensitive( not_rounded, FALSE );
2004 } else {
2005 gtk_action_set_sensitive( not_rounded, TRUE );
2006 }
2007 }
2010 static void
2011 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2012 void (*setter)(SPRect *, gdouble))
2013 {
2014 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2016 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2017 SPUnit const *unit = tracker->getActiveUnit();
2019 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2020 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2021 }
2023 // quit if run by the attr_changed listener
2024 if (g_object_get_data( tbl, "freeze" )) {
2025 return;
2026 }
2028 // in turn, prevent listener from responding
2029 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2031 bool modmade = false;
2032 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2033 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2034 if (SP_IS_RECT(items->data)) {
2035 if (adj->value != 0) {
2036 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2037 } else {
2038 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2039 }
2040 modmade = true;
2041 }
2042 }
2044 sp_rtb_sensitivize( tbl );
2046 if (modmade) {
2047 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2048 _("Change rectangle"));
2049 }
2051 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2052 }
2054 static void
2055 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2056 {
2057 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2058 }
2060 static void
2061 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2062 {
2063 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2064 }
2066 static void
2067 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2068 {
2069 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2070 }
2072 static void
2073 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2074 {
2075 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2076 }
2080 static void
2081 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2082 {
2083 GtkAdjustment *adj = 0;
2085 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2086 gtk_adjustment_set_value(adj, 0.0);
2087 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2088 gtk_adjustment_value_changed(adj);
2090 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2091 gtk_adjustment_set_value(adj, 0.0);
2092 gtk_adjustment_value_changed(adj);
2094 sp_rtb_sensitivize( obj );
2095 }
2097 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2098 gchar const */*old_value*/, gchar const */*new_value*/,
2099 bool /*is_interactive*/, gpointer data)
2100 {
2101 GObject *tbl = G_OBJECT(data);
2103 // quit if run by the _changed callbacks
2104 if (g_object_get_data( tbl, "freeze" )) {
2105 return;
2106 }
2108 // in turn, prevent callbacks from responding
2109 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2111 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2112 SPUnit const *unit = tracker->getActiveUnit();
2114 gpointer item = g_object_get_data( tbl, "item" );
2115 if (item && SP_IS_RECT(item)) {
2116 {
2117 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2118 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2119 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2120 }
2122 {
2123 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2124 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2125 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2126 }
2128 {
2129 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2130 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2131 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2132 }
2134 {
2135 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2136 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2137 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2138 }
2139 }
2141 sp_rtb_sensitivize( tbl );
2143 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2144 }
2147 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2148 NULL, /* child_added */
2149 NULL, /* child_removed */
2150 rect_tb_event_attr_changed,
2151 NULL, /* content_changed */
2152 NULL /* order_changed */
2153 };
2155 /**
2156 * \param selection should not be NULL.
2157 */
2158 static void
2159 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2160 {
2161 int n_selected = 0;
2162 Inkscape::XML::Node *repr = NULL;
2163 SPItem *item = NULL;
2165 if ( g_object_get_data( tbl, "repr" ) ) {
2166 g_object_set_data( tbl, "item", NULL );
2167 }
2168 purge_repr_listener( tbl, tbl );
2170 for (GSList const *items = selection->itemList();
2171 items != NULL;
2172 items = items->next) {
2173 if (SP_IS_RECT((SPItem *) items->data)) {
2174 n_selected++;
2175 item = (SPItem *) items->data;
2176 repr = SP_OBJECT_REPR(item);
2177 }
2178 }
2180 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2182 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2184 if (n_selected == 0) {
2185 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2187 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2188 gtk_action_set_sensitive(w, FALSE);
2189 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2190 gtk_action_set_sensitive(h, FALSE);
2192 } else if (n_selected == 1) {
2193 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2194 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2196 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2197 gtk_action_set_sensitive(w, TRUE);
2198 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2199 gtk_action_set_sensitive(h, TRUE);
2201 if (repr) {
2202 g_object_set_data( tbl, "repr", repr );
2203 g_object_set_data( tbl, "item", item );
2204 Inkscape::GC::anchor(repr);
2205 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2206 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2207 }
2208 } else {
2209 // FIXME: implement averaging of all parameters for multiple selected
2210 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2211 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2212 sp_rtb_sensitivize( tbl );
2213 }
2214 }
2217 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2218 {
2219 EgeAdjustmentAction* eact = 0;
2221 {
2222 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2223 ege_output_action_set_use_markup( act, TRUE );
2224 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2225 g_object_set_data( holder, "mode_action", act );
2226 }
2228 // rx/ry units menu: create
2229 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2230 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2231 // fixme: add % meaning per cent of the width/height
2232 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2233 g_object_set_data( holder, "tracker", tracker );
2235 /* W */
2236 {
2237 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2238 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2239 eact = create_adjustment_action( "RectWidthAction",
2240 _("Width"), _("W:"), _("Width of rectangle"),
2241 "tools.shapes.rect", "width", 0,
2242 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2243 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2244 labels, values, G_N_ELEMENTS(labels),
2245 sp_rtb_width_value_changed );
2246 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2247 g_object_set_data( holder, "width_action", eact );
2248 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2249 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2250 }
2252 /* H */
2253 {
2254 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2255 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2256 eact = create_adjustment_action( "RectHeightAction",
2257 _("Height"), _("H:"), _("Height of rectangle"),
2258 "tools.shapes.rect", "height", 0,
2259 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2260 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2261 labels, values, G_N_ELEMENTS(labels),
2262 sp_rtb_height_value_changed );
2263 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2264 g_object_set_data( holder, "height_action", eact );
2265 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2266 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2267 }
2269 /* rx */
2270 {
2271 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2272 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2273 eact = create_adjustment_action( "RadiusXAction",
2274 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2275 "tools.shapes.rect", "rx", 0,
2276 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2277 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2278 labels, values, G_N_ELEMENTS(labels),
2279 sp_rtb_rx_value_changed);
2280 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2281 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2282 }
2284 /* ry */
2285 {
2286 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2287 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2288 eact = create_adjustment_action( "RadiusYAction",
2289 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2290 "tools.shapes.rect", "ry", 0,
2291 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2292 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2293 labels, values, G_N_ELEMENTS(labels),
2294 sp_rtb_ry_value_changed);
2295 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2296 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2297 }
2299 // add the units menu
2300 {
2301 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2302 gtk_action_group_add_action( mainActions, act );
2303 }
2305 /* Reset */
2306 {
2307 InkAction* inky = ink_action_new( "RectResetAction",
2308 _("Not rounded"),
2309 _("Make corners sharp"),
2310 "squared_corner",
2311 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2312 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2313 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2314 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2315 g_object_set_data( holder, "not_rounded", inky );
2316 }
2318 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2319 sp_rtb_sensitivize( holder );
2321 sigc::connection *connection = new sigc::connection(
2322 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2323 );
2324 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2325 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2326 }
2328 //########################
2329 //## 3D Box ##
2330 //########################
2332 // normalize angle so that it lies in the interval [0,360]
2333 static double box3d_normalize_angle (double a) {
2334 double angle = a + ((int) (a/360.0))*360;
2335 if (angle < 0) {
2336 angle += 360.0;
2337 }
2338 return angle;
2339 }
2341 static void
2342 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2343 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2344 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2345 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2346 // are reset).
2347 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2349 if (is_infinite) {
2350 gtk_toggle_action_set_active(tact, TRUE);
2351 gtk_action_set_sensitive(act, TRUE);
2353 double angle = persp3d_get_infinite_angle(persp, axis);
2354 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2355 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2356 }
2357 } else {
2358 gtk_toggle_action_set_active(tact, FALSE);
2359 gtk_action_set_sensitive(act, FALSE);
2360 }
2361 }
2363 static void
2364 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2365 if (!persp_repr) {
2366 g_print ("No perspective given to box3d_resync_toolbar().\n");
2367 return;
2368 }
2370 GtkWidget *tbl = GTK_WIDGET(data);
2371 GtkAdjustment *adj = 0;
2372 GtkAction *act = 0;
2373 GtkToggleAction *tact = 0;
2374 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2375 {
2376 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2377 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2378 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2380 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2381 }
2382 {
2383 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2384 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2385 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2387 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2388 }
2389 {
2390 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2391 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2392 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2394 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2395 }
2396 }
2398 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2399 gchar const */*old_value*/, gchar const */*new_value*/,
2400 bool /*is_interactive*/, gpointer data)
2401 {
2402 GtkWidget *tbl = GTK_WIDGET(data);
2404 // quit if run by the attr_changed listener
2405 // note: it used to work without the differently called freeze_ attributes (here and in
2406 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2407 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2408 return;
2409 }
2411 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2412 // sp_document_maybe_done() when the document is undo insensitive)
2413 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2415 // TODO: Only update the appropriate part of the toolbar
2416 // if (!strcmp(name, "inkscape:vp_z")) {
2417 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2418 // }
2420 Persp3D *persp = persp3d_get_from_repr(repr);
2421 persp3d_update_box_reprs(persp);
2423 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2424 }
2426 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2427 {
2428 NULL, /* child_added */
2429 NULL, /* child_removed */
2430 box3d_persp_tb_event_attr_changed,
2431 NULL, /* content_changed */
2432 NULL /* order_changed */
2433 };
2435 /**
2436 * \param selection Should not be NULL.
2437 */
2438 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2439 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2440 static void
2441 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2442 {
2443 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2444 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2445 // update the perspectives with infinite VPs and leave the other ones untouched).
2447 Inkscape::XML::Node *persp_repr = NULL;
2448 purge_repr_listener(tbl, tbl);
2450 SPItem *item = selection->singleItem();
2451 if (item && SP_IS_BOX3D(item)) {
2452 // FIXME: Also deal with multiple selected boxes
2453 SPBox3D *box = SP_BOX3D(item);
2454 Persp3D *persp = box3d_get_perspective(box);
2455 persp_repr = SP_OBJECT_REPR(persp);
2456 if (persp_repr) {
2457 g_object_set_data(tbl, "repr", persp_repr);
2458 Inkscape::GC::anchor(persp_repr);
2459 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2460 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2461 }
2463 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2464 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2466 box3d_resync_toolbar(persp_repr, tbl);
2467 }
2468 }
2470 static void
2471 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2472 {
2473 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2474 SPDocument *document = sp_desktop_document(desktop);
2476 // quit if run by the attr_changed listener
2477 // note: it used to work without the differently called freeze_ attributes (here and in
2478 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2479 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2480 return;
2481 }
2483 // in turn, prevent listener from responding
2484 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2486 //Persp3D *persp = document->current_persp3d;
2487 std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
2488 if (sel_persps.empty()) {
2489 // this can happen when the document is created; we silently ignore it
2490 return;
2491 }
2492 Persp3D *persp = *(sel_persps.begin());
2494 persp->tmat.set_infinite_direction (axis, adj->value);
2495 SP_OBJECT(persp)->updateRepr();
2497 // TODO: use the correct axis here, too
2498 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2500 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2501 }
2504 static void
2505 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2506 {
2507 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2508 }
2510 static void
2511 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2512 {
2513 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2514 }
2516 static void
2517 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2518 {
2519 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2520 }
2523 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction *box3d_angle, Proj::Axis axis )
2524 {
2525 // TODO: Take all selected perspectives into account
2526 std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
2527 if (sel_persps.empty()) {
2528 // this can happen when the document is created; we silently ignore it
2529 return;
2530 }
2531 Persp3D *persp = *(sel_persps.begin());
2533 bool set_infinite = gtk_toggle_action_get_active(act);
2534 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2535 }
2537 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2538 {
2539 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2540 }
2542 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2543 {
2544 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2545 }
2547 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2548 {
2549 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2550 }
2552 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2553 {
2554 EgeAdjustmentAction* eact = 0;
2555 SPDocument *document = sp_desktop_document (desktop);
2556 Persp3D *persp = document->current_persp3d;
2558 EgeAdjustmentAction* box3d_angle_x = 0;
2559 EgeAdjustmentAction* box3d_angle_y = 0;
2560 EgeAdjustmentAction* box3d_angle_z = 0;
2562 /* Angle X */
2563 {
2564 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2565 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2566 eact = create_adjustment_action( "3DBoxAngleXAction",
2567 _("Angle in X direction"), _("Angle X:"),
2568 // Translators: PL is short for 'perspective line'
2569 _("Angle of PLs in X direction"),
2570 "tools.shapes.3dbox", "box3d_angle_x", 30,
2571 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2572 -360.0, 360.0, 1.0, 10.0,
2573 labels, values, G_N_ELEMENTS(labels),
2574 box3d_angle_x_value_changed );
2575 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2576 g_object_set_data( holder, "box3d_angle_x_action", eact );
2577 box3d_angle_x = eact;
2578 }
2580 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2581 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2582 } else {
2583 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2584 }
2587 /* VP X state */
2588 {
2589 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2590 // Translators: VP is short for 'vanishing point'
2591 _("State of VP in X direction"),
2592 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2593 "toggle_vp_x",
2594 Inkscape::ICON_SIZE_DECORATION );
2595 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2596 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2597 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2598 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2599 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2600 }
2602 /* Angle Y */
2603 {
2604 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2605 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2606 eact = create_adjustment_action( "3DBoxAngleYAction",
2607 _("Angle in Y direction"), _("Angle Y:"),
2608 // Translators: PL is short for 'perspective line'
2609 _("Angle of PLs in Y direction"),
2610 "tools.shapes.3dbox", "box3d_angle_y", 30,
2611 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2612 -360.0, 360.0, 1.0, 10.0,
2613 labels, values, G_N_ELEMENTS(labels),
2614 box3d_angle_y_value_changed );
2615 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2616 g_object_set_data( holder, "box3d_angle_y_action", eact );
2617 box3d_angle_y = eact;
2618 }
2620 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2621 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2622 } else {
2623 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2624 }
2626 /* VP Y state */
2627 {
2628 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2629 // Translators: VP is short for 'vanishing point'
2630 _("State of VP in Y direction"),
2631 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2632 "toggle_vp_y",
2633 Inkscape::ICON_SIZE_DECORATION );
2634 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2635 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2636 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2637 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2638 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2639 }
2641 /* Angle Z */
2642 {
2643 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2644 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2645 eact = create_adjustment_action( "3DBoxAngleZAction",
2646 _("Angle in Z direction"), _("Angle Z:"),
2647 // Translators: PL is short for 'perspective line'
2648 _("Angle of PLs in Z direction"),
2649 "tools.shapes.3dbox", "box3d_angle_z", 30,
2650 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2651 -360.0, 360.0, 1.0, 10.0,
2652 labels, values, G_N_ELEMENTS(labels),
2653 box3d_angle_z_value_changed );
2654 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2655 g_object_set_data( holder, "box3d_angle_z_action", eact );
2656 box3d_angle_z = eact;
2657 }
2659 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2660 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2661 } else {
2662 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2663 }
2665 /* VP Z state */
2666 {
2667 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2668 // Translators: VP is short for 'vanishing point'
2669 _("State of VP in Z direction"),
2670 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2671 "toggle_vp_z",
2672 Inkscape::ICON_SIZE_DECORATION );
2673 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2674 g_object_set_data( holder, "box3d_vp_z_state_action", act );
2675 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2676 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2677 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2678 }
2680 sigc::connection *connection = new sigc::connection(
2681 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2682 );
2683 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2684 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2685 }
2687 //########################
2688 //## Spiral ##
2689 //########################
2691 static void
2692 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2693 {
2694 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2696 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2697 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2698 }
2700 // quit if run by the attr_changed listener
2701 if (g_object_get_data( tbl, "freeze" )) {
2702 return;
2703 }
2705 // in turn, prevent listener from responding
2706 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2708 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2710 bool modmade = false;
2711 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2712 items != NULL;
2713 items = items->next)
2714 {
2715 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2716 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2717 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2718 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2719 modmade = true;
2720 }
2721 }
2723 g_free(namespaced_name);
2725 if (modmade) {
2726 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2727 _("Change spiral"));
2728 }
2730 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2731 }
2733 static void
2734 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2735 {
2736 sp_spl_tb_value_changed(adj, tbl, "revolution");
2737 }
2739 static void
2740 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2741 {
2742 sp_spl_tb_value_changed(adj, tbl, "expansion");
2743 }
2745 static void
2746 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2747 {
2748 sp_spl_tb_value_changed(adj, tbl, "t0");
2749 }
2751 static void
2752 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2753 {
2754 GtkWidget *tbl = GTK_WIDGET(obj);
2756 GtkAdjustment *adj;
2758 // fixme: make settable
2759 gdouble rev = 5;
2760 gdouble exp = 1.0;
2761 gdouble t0 = 0.0;
2763 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2764 gtk_adjustment_set_value(adj, rev);
2765 gtk_adjustment_value_changed(adj);
2767 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2768 gtk_adjustment_set_value(adj, exp);
2769 gtk_adjustment_value_changed(adj);
2771 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2772 gtk_adjustment_set_value(adj, t0);
2773 gtk_adjustment_value_changed(adj);
2775 spinbutton_defocus(GTK_OBJECT(tbl));
2776 }
2779 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2780 gchar const */*old_value*/, gchar const */*new_value*/,
2781 bool /*is_interactive*/, gpointer data)
2782 {
2783 GtkWidget *tbl = GTK_WIDGET(data);
2785 // quit if run by the _changed callbacks
2786 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2787 return;
2788 }
2790 // in turn, prevent callbacks from responding
2791 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2793 GtkAdjustment *adj;
2794 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2795 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2797 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2798 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2800 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2801 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2803 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2804 }
2807 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2808 NULL, /* child_added */
2809 NULL, /* child_removed */
2810 spiral_tb_event_attr_changed,
2811 NULL, /* content_changed */
2812 NULL /* order_changed */
2813 };
2815 static void
2816 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2817 {
2818 int n_selected = 0;
2819 Inkscape::XML::Node *repr = NULL;
2821 purge_repr_listener( tbl, tbl );
2823 for (GSList const *items = selection->itemList();
2824 items != NULL;
2825 items = items->next)
2826 {
2827 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2828 n_selected++;
2829 repr = SP_OBJECT_REPR((SPItem *) items->data);
2830 }
2831 }
2833 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2835 if (n_selected == 0) {
2836 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2837 } else if (n_selected == 1) {
2838 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2840 if (repr) {
2841 g_object_set_data( tbl, "repr", repr );
2842 Inkscape::GC::anchor(repr);
2843 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2844 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2845 }
2846 } else {
2847 // FIXME: implement averaging of all parameters for multiple selected
2848 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2849 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2850 }
2851 }
2854 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2855 {
2856 EgeAdjustmentAction* eact = 0;
2858 {
2859 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2860 ege_output_action_set_use_markup( act, TRUE );
2861 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2862 g_object_set_data( holder, "mode_action", act );
2863 }
2865 /* Revolution */
2866 {
2867 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2868 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2869 eact = create_adjustment_action( "SpiralRevolutionAction",
2870 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
2871 "tools.shapes.spiral", "revolution", 3.0,
2872 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2873 0.01, 1024.0, 0.1, 1.0,
2874 labels, values, G_N_ELEMENTS(labels),
2875 sp_spl_tb_revolution_value_changed, 1, 2);
2876 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2877 }
2879 /* Expansion */
2880 {
2881 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2882 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2883 eact = create_adjustment_action( "SpiralExpansionAction",
2884 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2885 "tools.shapes.spiral", "expansion", 1.0,
2886 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2887 0.0, 1000.0, 0.01, 1.0,
2888 labels, values, G_N_ELEMENTS(labels),
2889 sp_spl_tb_expansion_value_changed);
2890 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2891 }
2893 /* T0 */
2894 {
2895 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2896 gdouble values[] = {0, 0.5, 0.9};
2897 eact = create_adjustment_action( "SpiralT0Action",
2898 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2899 "tools.shapes.spiral", "t0", 0.0,
2900 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2901 0.0, 0.999, 0.01, 1.0,
2902 labels, values, G_N_ELEMENTS(labels),
2903 sp_spl_tb_t0_value_changed);
2904 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2905 }
2907 /* Reset */
2908 {
2909 InkAction* inky = ink_action_new( "SpiralResetAction",
2910 _("Defaults"),
2911 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2912 GTK_STOCK_CLEAR,
2913 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2914 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2915 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2916 }
2919 sigc::connection *connection = new sigc::connection(
2920 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2921 );
2922 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2923 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2924 }
2926 //########################
2927 //## Pen/Pencil ##
2928 //########################
2931 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2932 {
2933 // Put stuff here
2934 }
2936 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2937 {
2938 // Put stuff here
2939 }
2941 //########################
2942 //## Tweak ##
2943 //########################
2945 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2946 {
2947 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2948 }
2950 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2951 {
2952 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2953 }
2955 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
2956 {
2957 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2958 }
2960 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2961 {
2962 int mode = ege_select_one_action_get_active( act );
2963 prefs_set_int_attribute("tools.tweak", "mode", mode);
2965 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2966 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2967 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2968 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2969 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2970 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2971 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
2972 if (doh) gtk_action_set_sensitive (doh, TRUE);
2973 if (dos) gtk_action_set_sensitive (dos, TRUE);
2974 if (dol) gtk_action_set_sensitive (dol, TRUE);
2975 if (doo) gtk_action_set_sensitive (doo, TRUE);
2976 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
2977 if (fid) gtk_action_set_sensitive (fid, FALSE);
2978 } else {
2979 if (doh) gtk_action_set_sensitive (doh, FALSE);
2980 if (dos) gtk_action_set_sensitive (dos, FALSE);
2981 if (dol) gtk_action_set_sensitive (dol, FALSE);
2982 if (doo) gtk_action_set_sensitive (doo, FALSE);
2983 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
2984 if (fid) gtk_action_set_sensitive (fid, TRUE);
2985 }
2986 }
2988 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2989 {
2990 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
2991 }
2993 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
2994 bool show = gtk_toggle_action_get_active( act );
2995 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
2996 }
2997 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
2998 bool show = gtk_toggle_action_get_active( act );
2999 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3000 }
3001 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3002 bool show = gtk_toggle_action_get_active( act );
3003 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3004 }
3005 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3006 bool show = gtk_toggle_action_get_active( act );
3007 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3008 }
3010 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3011 {
3012 {
3013 /* Width */
3014 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3015 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3016 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3017 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3018 "tools.tweak", "width", 15,
3019 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3020 1, 100, 1.0, 10.0,
3021 labels, values, G_N_ELEMENTS(labels),
3022 sp_tweak_width_value_changed, 0.01, 0, 100 );
3023 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3024 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3025 }
3028 {
3029 /* Force */
3030 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3031 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3032 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3033 _("Force"), _("Force:"), _("The force of the tweak action"),
3034 "tools.tweak", "force", 20,
3035 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3036 1, 100, 1.0, 10.0,
3037 labels, values, G_N_ELEMENTS(labels),
3038 sp_tweak_force_value_changed, 0.01, 0, 100 );
3039 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3040 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3041 }
3043 /* Mode */
3044 {
3045 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3047 GtkTreeIter iter;
3048 gtk_list_store_append( model, &iter );
3049 gtk_list_store_set( model, &iter,
3050 0, _("Push mode"),
3051 1, _("Push parts of paths in any direction"),
3052 2, "tweak_push_mode",
3053 -1 );
3055 gtk_list_store_append( model, &iter );
3056 gtk_list_store_set( model, &iter,
3057 0, _("Shrink mode"),
3058 1, _("Shrink (inset) parts of paths"),
3059 2, "tweak_shrink_mode",
3060 -1 );
3062 gtk_list_store_append( model, &iter );
3063 gtk_list_store_set( model, &iter,
3064 0, _("Grow mode"),
3065 1, _("Grow (outset) parts of paths"),
3066 2, "tweak_grow_mode",
3067 -1 );
3069 gtk_list_store_append( model, &iter );
3070 gtk_list_store_set( model, &iter,
3071 0, _("Attract mode"),
3072 1, _("Attract parts of paths towards cursor"),
3073 2, "tweak_attract_mode",
3074 -1 );
3076 gtk_list_store_append( model, &iter );
3077 gtk_list_store_set( model, &iter,
3078 0, _("Repel mode"),
3079 1, _("Repel parts of paths from cursor"),
3080 2, "tweak_repel_mode",
3081 -1 );
3083 gtk_list_store_append( model, &iter );
3084 gtk_list_store_set( model, &iter,
3085 0, _("Roughen mode"),
3086 1, _("Roughen parts of paths"),
3087 2, "tweak_roughen_mode",
3088 -1 );
3090 gtk_list_store_append( model, &iter );
3091 gtk_list_store_set( model, &iter,
3092 0, _("Color paint mode"),
3093 1, _("Paint the tool's color upon selected objects"),
3094 2, "tweak_colorpaint_mode",
3095 -1 );
3097 gtk_list_store_append( model, &iter );
3098 gtk_list_store_set( model, &iter,
3099 0, _("Color jitter mode"),
3100 1, _("Jitter the colors of selected objects"),
3101 2, "tweak_colorjitter_mode",
3102 -1 );
3104 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3105 g_object_set( act, "short_label", _("Mode:"), NULL );
3106 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3107 g_object_set_data( holder, "mode_action", act );
3109 ege_select_one_action_set_appearance( act, "full" );
3110 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3111 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3112 ege_select_one_action_set_icon_column( act, 2 );
3113 ege_select_one_action_set_tooltip_column( act, 1 );
3115 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3116 ege_select_one_action_set_active( act, mode );
3117 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3119 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3120 }
3122 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3124 {
3125 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3126 ege_output_action_set_use_markup( act, TRUE );
3127 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3128 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3129 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3130 g_object_set_data( holder, "tweak_channels_label", act);
3131 }
3133 {
3134 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3135 _("Hue"),
3136 _("In color mode, act on objects' hue"),
3137 NULL,
3138 Inkscape::ICON_SIZE_DECORATION );
3139 g_object_set( act, "short_label", _("H"), NULL );
3140 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3141 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3142 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3143 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3144 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3145 g_object_set_data( holder, "tweak_doh", act);
3146 }
3147 {
3148 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3149 _("Saturation"),
3150 _("In color mode, act on objects' saturation"),
3151 NULL,
3152 Inkscape::ICON_SIZE_DECORATION );
3153 g_object_set( act, "short_label", _("S"), NULL );
3154 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3155 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3156 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3157 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3158 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3159 g_object_set_data( holder, "tweak_dos", act );
3160 }
3161 {
3162 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3163 _("Lightness"),
3164 _("In color mode, act on objects' lightness"),
3165 NULL,
3166 Inkscape::ICON_SIZE_DECORATION );
3167 g_object_set( act, "short_label", _("L"), NULL );
3168 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3169 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3170 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3171 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3172 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3173 g_object_set_data( holder, "tweak_dol", act );
3174 }
3175 {
3176 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3177 _("Opacity"),
3178 _("In color mode, act on objects' opacity"),
3179 NULL,
3180 Inkscape::ICON_SIZE_DECORATION );
3181 g_object_set( act, "short_label", _("O"), NULL );
3182 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3183 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3184 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3185 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3186 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3187 g_object_set_data( holder, "tweak_doo", act );
3188 }
3190 { /* Fidelity */
3191 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3192 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3193 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3194 _("Fidelity"), _("Fidelity:"),
3195 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3196 "tools.tweak", "fidelity", 50,
3197 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3198 1, 100, 1.0, 10.0,
3199 labels, values, G_N_ELEMENTS(labels),
3200 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3201 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3202 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3203 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3204 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3205 g_object_set_data( holder, "tweak_fidelity", eact );
3206 }
3209 /* Use Pressure button */
3210 {
3211 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3212 _("Pressure"),
3213 _("Use the pressure of the input device to alter the force of tweak action"),
3214 "use_pressure",
3215 Inkscape::ICON_SIZE_DECORATION );
3216 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3217 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3218 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3219 }
3221 }
3224 //########################
3225 //## Calligraphy ##
3226 //########################
3228 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3229 {
3230 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3231 }
3233 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3234 {
3235 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3236 }
3238 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3239 {
3240 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3241 }
3243 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3244 {
3245 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3246 }
3248 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3249 {
3250 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3251 }
3253 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3254 {
3255 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3256 }
3258 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3259 {
3260 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3261 }
3263 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3264 {
3265 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3266 }
3268 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3269 {
3270 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3271 }
3273 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3274 {
3275 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3276 }
3278 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3279 {
3280 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3282 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3283 }
3285 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3286 {
3287 // FIXME: make defaults settable via Inkscape Options
3288 struct KeyValue {
3289 char const *key;
3290 double value;
3291 } const key_values[] = {
3292 {"mass", 0.02},
3293 {"wiggle", 0.0},
3294 {"angle", 30.0},
3295 {"width", 15},
3296 {"thinning", 0.1},
3297 {"tremor", 0.0},
3298 {"flatness", 0.9},
3299 {"cap_rounding", 0.0}
3300 };
3302 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3303 KeyValue const &kv = key_values[i];
3304 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3305 if ( adj ) {
3306 gtk_adjustment_set_value(adj, kv.value);
3307 }
3308 }
3309 }
3312 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3313 {
3314 {
3315 EgeAdjustmentAction* calligraphy_angle = 0;
3317 {
3318 /* Width */
3319 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3320 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3321 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3322 _("Pen Width"), _("Width:"),
3323 _("The width of the calligraphic pen (relative to the visible canvas area)"),
3324 "tools.calligraphic", "width", 15,
3325 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3326 1, 100, 1.0, 10.0,
3327 labels, values, G_N_ELEMENTS(labels),
3328 sp_ddc_width_value_changed, 0.01, 0, 100 );
3329 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3330 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3331 }
3333 {
3334 /* Thinning */
3335 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3336 gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3337 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3338 _("Stroke Thinning"), _("Thinning:"),
3339 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3340 "tools.calligraphic", "thinning", 0.1,
3341 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3342 -1.0, 1.0, 0.01, 0.1,
3343 labels, values, G_N_ELEMENTS(labels),
3344 sp_ddc_velthin_value_changed, 0.01, 2);
3345 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3346 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3347 }
3349 {
3350 /* Angle */
3351 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3352 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3353 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3354 _("Pen Angle"), _("Angle:"),
3355 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3356 "tools.calligraphic", "angle", 30,
3357 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3358 -90.0, 90.0, 1.0, 10.0,
3359 labels, values, G_N_ELEMENTS(labels),
3360 sp_ddc_angle_value_changed, 1, 0 );
3361 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3362 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3363 calligraphy_angle = eact;
3364 }
3366 {
3367 /* Fixation */
3368 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3369 gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3370 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3371 _("Fixation"), _("Fixation:"),
3372 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3373 "tools.calligraphic", "flatness", 0.9,
3374 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3375 0.0, 1.0, 0.01, 0.1,
3376 labels, values, G_N_ELEMENTS(labels),
3377 sp_ddc_flatness_value_changed, 0.01, 2 );
3378 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3379 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3380 }
3382 {
3383 /* Cap Rounding */
3384 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3385 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3386 // TRANSLATORS: "cap" means "end" (both start and finish) here
3387 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3388 _("Cap rounding"), _("Caps:"),
3389 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3390 "tools.calligraphic", "cap_rounding", 0.0,
3391 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3392 0.0, 5.0, 0.01, 0.1,
3393 labels, values, G_N_ELEMENTS(labels),
3394 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3395 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3396 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3397 }
3399 {
3400 /* Tremor */
3401 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3402 gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3403 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3404 _("Stroke Tremor"), _("Tremor:"),
3405 _("Increase to make strokes rugged and trembling"),
3406 "tools.calligraphic", "tremor", 0.0,
3407 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3408 0.0, 1.0, 0.01, 0.1,
3409 labels, values, G_N_ELEMENTS(labels),
3410 sp_ddc_tremor_value_changed, 0.01, 2 );
3412 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3413 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3414 }
3416 {
3417 /* Wiggle */
3418 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3419 gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3420 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3421 _("Pen Wiggle"), _("Wiggle:"),
3422 _("Increase to make the pen waver and wiggle"),
3423 "tools.calligraphic", "wiggle", 0.0,
3424 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3425 0.0, 1.0, 0.01, 0.1,
3426 labels, values, G_N_ELEMENTS(labels),
3427 sp_ddc_wiggle_value_changed, 0.01, 2 );
3428 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3429 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3430 }
3432 {
3433 /* Mass */
3434 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3435 gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3436 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3437 _("Pen Mass"), _("Mass:"),
3438 _("Increase to make the pen drag behind, as if slowed by inertia"),
3439 "tools.calligraphic", "mass", 0.02,
3440 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3441 0.0, 1.0, 0.01, 0.1,
3442 labels, values, G_N_ELEMENTS(labels),
3443 sp_ddc_mass_value_changed, 0.01, 2 );
3444 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3445 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3446 }
3449 /* Trace Background button */
3450 {
3451 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3452 _("Trace Background"),
3453 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3454 "trace_background",
3455 Inkscape::ICON_SIZE_DECORATION );
3456 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3457 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3458 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3459 }
3461 /* Use Pressure button */
3462 {
3463 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3464 _("Pressure"),
3465 _("Use the pressure of the input device to alter the width of the pen"),
3466 "use_pressure",
3467 Inkscape::ICON_SIZE_DECORATION );
3468 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3469 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3470 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3471 }
3473 /* Use Tilt button */
3474 {
3475 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3476 _("Tilt"),
3477 _("Use the tilt of the input device to alter the angle of the pen's nib"),
3478 "use_tilt",
3479 Inkscape::ICON_SIZE_DECORATION );
3480 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3481 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3482 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3483 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3484 }
3486 /* Reset */
3487 {
3488 GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3489 _("Defaults"),
3490 _("Reset all parameters to defaults"),
3491 GTK_STOCK_CLEAR );
3492 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3493 gtk_action_group_add_action( mainActions, act );
3494 gtk_action_set_sensitive( act, TRUE );
3495 }
3496 }
3497 }
3500 //########################
3501 //## Circle / Arc ##
3502 //########################
3504 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3505 {
3506 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3507 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3509 if (v1 == 0 && v2 == 0) {
3510 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3511 gtk_action_set_sensitive( ocb, FALSE );
3512 gtk_action_set_sensitive( make_whole, FALSE );
3513 }
3514 } else {
3515 gtk_action_set_sensitive( ocb, TRUE );
3516 gtk_action_set_sensitive( make_whole, TRUE );
3517 }
3518 }
3520 static void
3521 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3522 {
3523 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3525 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3526 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3527 }
3529 // quit if run by the attr_changed listener
3530 if (g_object_get_data( tbl, "freeze" )) {
3531 return;
3532 }
3534 // in turn, prevent listener from responding
3535 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3537 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3539 bool modmade = false;
3540 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3541 items != NULL;
3542 items = items->next)
3543 {
3544 SPItem *item = SP_ITEM(items->data);
3546 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3548 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3549 SPArc *arc = SP_ARC(item);
3551 if (!strcmp(value_name, "start"))
3552 ge->start = (adj->value * M_PI)/ 180;
3553 else
3554 ge->end = (adj->value * M_PI)/ 180;
3556 sp_genericellipse_normalize(ge);
3557 ((SPObject *)arc)->updateRepr();
3558 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3560 modmade = true;
3561 }
3562 }
3564 g_free(namespaced_name);
3566 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3568 sp_arctb_sensitivize( tbl, adj->value, other->value );
3570 if (modmade) {
3571 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3572 _("Arc: Change start/end"));
3573 }
3575 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3576 }
3579 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
3580 {
3581 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
3582 }
3584 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3585 {
3586 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
3587 }
3589 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3590 {
3591 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3592 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3593 if ( ege_select_one_action_get_active( act ) != 0 ) {
3594 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3595 } else {
3596 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3597 }
3598 }
3600 // quit if run by the attr_changed listener
3601 if (g_object_get_data( tbl, "freeze" )) {
3602 return;
3603 }
3605 // in turn, prevent listener from responding
3606 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3608 bool modmade = false;
3610 if ( ege_select_one_action_get_active(act) != 0 ) {
3611 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3612 items != NULL;
3613 items = items->next)
3614 {
3615 if (SP_IS_ARC((SPItem *) items->data)) {
3616 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3617 repr->setAttribute("sodipodi:open", "true");
3618 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3619 modmade = true;
3620 }
3621 }
3622 } else {
3623 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3624 items != NULL;
3625 items = items->next)
3626 {
3627 if (SP_IS_ARC((SPItem *) items->data)) {
3628 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3629 repr->setAttribute("sodipodi:open", NULL);
3630 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3631 modmade = true;
3632 }
3633 }
3634 }
3636 if (modmade) {
3637 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3638 _("Arc: Change open/closed"));
3639 }
3641 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3642 }
3644 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3645 {
3646 GtkAdjustment *adj;
3647 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3648 gtk_adjustment_set_value(adj, 0.0);
3649 gtk_adjustment_value_changed(adj);
3651 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3652 gtk_adjustment_set_value(adj, 0.0);
3653 gtk_adjustment_value_changed(adj);
3655 spinbutton_defocus( GTK_OBJECT(obj) );
3656 }
3658 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3659 gchar const */*old_value*/, gchar const */*new_value*/,
3660 bool /*is_interactive*/, gpointer data)
3661 {
3662 GObject *tbl = G_OBJECT(data);
3664 // quit if run by the _changed callbacks
3665 if (g_object_get_data( tbl, "freeze" )) {
3666 return;
3667 }
3669 // in turn, prevent callbacks from responding
3670 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3672 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3673 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3675 GtkAdjustment *adj1,*adj2;
3676 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3677 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3678 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3679 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3681 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3683 char const *openstr = NULL;
3684 openstr = repr->attribute("sodipodi:open");
3685 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3687 if (openstr) {
3688 ege_select_one_action_set_active( ocb, 1 );
3689 } else {
3690 ege_select_one_action_set_active( ocb, 0 );
3691 }
3693 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3694 }
3696 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3697 NULL, /* child_added */
3698 NULL, /* child_removed */
3699 arc_tb_event_attr_changed,
3700 NULL, /* content_changed */
3701 NULL /* order_changed */
3702 };
3705 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3706 {
3707 int n_selected = 0;
3708 Inkscape::XML::Node *repr = NULL;
3710 purge_repr_listener( tbl, tbl );
3712 for (GSList const *items = selection->itemList();
3713 items != NULL;
3714 items = items->next)
3715 {
3716 if (SP_IS_ARC((SPItem *) items->data)) {
3717 n_selected++;
3718 repr = SP_OBJECT_REPR((SPItem *) items->data);
3719 }
3720 }
3722 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3724 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3725 if (n_selected == 0) {
3726 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3727 } else if (n_selected == 1) {
3728 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3729 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3731 if (repr) {
3732 g_object_set_data( tbl, "repr", repr );
3733 Inkscape::GC::anchor(repr);
3734 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3735 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3736 }
3737 } else {
3738 // FIXME: implement averaging of all parameters for multiple selected
3739 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3740 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3741 sp_arctb_sensitivize( tbl, 1, 0 );
3742 }
3743 }
3746 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3747 {
3748 EgeAdjustmentAction* eact = 0;
3751 {
3752 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3753 ege_output_action_set_use_markup( act, TRUE );
3754 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3755 g_object_set_data( holder, "mode_action", act );
3756 }
3758 /* Start */
3759 {
3760 eact = create_adjustment_action( "ArcStartAction",
3761 _("Start"), _("Start:"),
3762 _("The angle (in degrees) from the horizontal to the arc's start point"),
3763 "tools.shapes.arc", "start", 0.0,
3764 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3765 -360.0, 360.0, 1.0, 10.0,
3766 0, 0, 0,
3767 sp_arctb_start_value_changed);
3768 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3769 }
3771 /* End */
3772 {
3773 eact = create_adjustment_action( "ArcEndAction",
3774 _("End"), _("End:"),
3775 _("The angle (in degrees) from the horizontal to the arc's end point"),
3776 "tools.shapes.arc", "end", 0.0,
3777 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3778 -360.0, 360.0, 1.0, 10.0,
3779 0, 0, 0,
3780 sp_arctb_end_value_changed);
3781 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3782 }
3784 /* Segments / Pie checkbox */
3785 {
3786 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3788 GtkTreeIter iter;
3789 gtk_list_store_append( model, &iter );
3790 gtk_list_store_set( model, &iter,
3791 0, _("Closed arc"),
3792 1, _("Switch to segment (closed shape with two radii)"),
3793 2, "circle_closed_arc",
3794 -1 );
3796 gtk_list_store_append( model, &iter );
3797 gtk_list_store_set( model, &iter,
3798 0, _("Open Arc"),
3799 1, _("Switch to arc (unclosed shape)"),
3800 2, "circle_open_arc",
3801 -1 );
3803 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3804 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3805 g_object_set_data( holder, "open_action", act );
3807 ege_select_one_action_set_appearance( act, "full" );
3808 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3809 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3810 ege_select_one_action_set_icon_column( act, 2 );
3811 ege_select_one_action_set_tooltip_column( act, 1 );
3813 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3814 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3815 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3816 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3817 }
3819 /* Make Whole */
3820 {
3821 InkAction* inky = ink_action_new( "ArcResetAction",
3822 _("Make whole"),
3823 _("Make the shape a whole ellipse, not arc or segment"),
3824 "reset_circle",
3825 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3826 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3827 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3828 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3829 g_object_set_data( holder, "make_whole", inky );
3830 }
3832 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3833 // sensitivize make whole and open checkbox
3834 {
3835 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3836 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3837 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3838 }
3841 sigc::connection *connection = new sigc::connection(
3842 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3843 );
3844 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3845 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3846 }
3851 // toggle button callbacks and updaters
3853 //########################
3854 //## Dropper ##
3855 //########################
3857 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3858 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3859 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3860 if ( set_action ) {
3861 if ( gtk_toggle_action_get_active( act ) ) {
3862 gtk_action_set_sensitive( set_action, TRUE );
3863 } else {
3864 gtk_action_set_sensitive( set_action, FALSE );
3865 }
3866 }
3868 spinbutton_defocus(GTK_OBJECT(tbl));
3869 }
3871 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3872 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3873 spinbutton_defocus(GTK_OBJECT(tbl));
3874 }
3877 /**
3878 * Dropper auxiliary toolbar construction and setup.
3879 *
3880 * TODO: Would like to add swatch of current color.
3881 * TODO: Add queue of last 5 or so colors selected with new swatches so that
3882 * can drag and drop places. Will provide a nice mixing palette.
3883 */
3884 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3885 {
3886 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3888 {
3889 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3890 _("Pick alpha"),
3891 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3892 "color_alpha_get",
3893 Inkscape::ICON_SIZE_DECORATION );
3894 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3895 g_object_set_data( holder, "pick_action", act );
3896 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3897 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3898 }
3900 {
3901 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3902 _("Set alpha"),
3903 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3904 "color_alpha_set",
3905 Inkscape::ICON_SIZE_DECORATION );
3906 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3907 g_object_set_data( holder, "set_action", act );
3908 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3909 // make sure it's disabled if we're not picking alpha
3910 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3911 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3912 }
3913 }
3916 //########################
3917 //## Text Toolbox ##
3918 //########################
3919 /*
3920 static void
3921 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3922 {
3923 //Call back for letter sizing spinbutton
3924 }
3926 static void
3927 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3928 {
3929 //Call back for line height spinbutton
3930 }
3932 static void
3933 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3934 {
3935 //Call back for horizontal kerning spinbutton
3936 }
3938 static void
3939 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3940 {
3941 //Call back for vertical kerning spinbutton
3942 }
3944 static void
3945 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3946 {
3947 //Call back for letter rotation spinbutton
3948 }*/
3950 namespace {
3952 bool visible = false;
3954 void
3955 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
3956 {
3957 SPStyle *query =
3958 sp_style_new (SP_ACTIVE_DOCUMENT);
3960 int result_fontspec =
3961 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
3963 int result_family =
3964 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3966 int result_style =
3967 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3969 int result_numbers =
3970 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3972 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3974 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3975 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3976 {
3977 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3979 if (repr)
3980 {
3981 sp_style_read_from_repr (query, repr);
3982 }
3983 else
3984 {
3985 return;
3986 }
3987 }
3989 if (query->text)
3990 {
3991 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
3992 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3993 gtk_entry_set_text (GTK_ENTRY (entry), "");
3995 } else if (query->text->font_specification.value || query->text->font_family.value) {
3997 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3999 // Get the font that corresponds
4000 Glib::ustring familyName;
4002 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4003 if (font) {
4004 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4005 font->Unref();
4006 font = NULL;
4007 }
4009 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4011 Gtk::TreePath path;
4012 try {
4013 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4014 } catch (...) {
4015 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4016 return;
4017 }
4019 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4020 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4022 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4024 gtk_tree_selection_select_path (tselection, path.gobj());
4025 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4027 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4028 }
4030 //Size
4031 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4032 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4033 g_object_set_data (tbl, "size-block", gpointer(1));
4034 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4035 g_object_set_data (tbl, "size-block", gpointer(0));
4036 free (str);
4038 //Anchor
4039 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4040 {
4041 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4042 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4043 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4044 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4045 }
4046 else
4047 {
4048 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4049 {
4050 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4051 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4052 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4053 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4054 }
4055 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4056 {
4057 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4058 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4059 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4060 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4061 }
4062 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4063 {
4064 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4065 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4066 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4067 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4068 }
4069 }
4071 //Style
4072 {
4073 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4075 gboolean active = gtk_toggle_button_get_active (button);
4076 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4078 if (active != check)
4079 {
4080 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4081 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4082 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4083 }
4084 }
4086 {
4087 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4089 gboolean active = gtk_toggle_button_get_active (button);
4090 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4092 if (active != check)
4093 {
4094 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4095 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4096 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4097 }
4098 }
4100 //Orientation
4101 //locking both buttons, changing one affect all group (both)
4102 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4103 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4105 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4106 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4108 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4109 {
4110 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4111 }
4112 else
4113 {
4114 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4115 }
4116 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4117 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4118 }
4120 sp_style_unref(query);
4121 }
4123 void
4124 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4125 {
4126 sp_text_toolbox_selection_changed (selection, tbl);
4127 }
4129 void
4130 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4131 {
4132 sp_text_toolbox_selection_changed (NULL, tbl);
4133 }
4135 void
4136 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
4137 GObject *tbl)
4138 {
4139 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4140 GtkTreeModel *model = 0;
4141 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4142 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4143 GtkTreeIter iter;
4144 char *family = 0;
4146 (void)popdown;
4148 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4149 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4151 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4152 return;
4153 }
4155 gtk_tree_model_get (model, &iter, 0, &family, -1);
4157 if (g_object_get_data (G_OBJECT (selection), "block"))
4158 {
4159 gtk_entry_set_text (GTK_ENTRY (entry), family);
4160 return;
4161 }
4163 gtk_entry_set_text (GTK_ENTRY (entry), family);
4165 SPStyle *query =
4166 sp_style_new (SP_ACTIVE_DOCUMENT);
4168 int result_fontspec =
4169 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4171 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4173 SPCSSAttr *css = sp_repr_css_attr_new ();
4176 // First try to get the font spec from the stored value
4177 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4179 if (fontSpec.empty()) {
4180 // Construct a new font specification if it does not yet exist
4181 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4182 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4183 fontFromStyle->Unref();
4184 }
4186 if (!fontSpec.empty()) {
4187 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4188 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4189 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4190 if (font) {
4191 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4193 // Set all the these just in case they were altered when finding the best
4194 // match for the new family and old style...
4196 gchar c[256];
4198 font->Family(c, 256);
4199 sp_repr_css_set_property (css, "font-family", c);
4201 font->Attribute( "weight", c, 256);
4202 sp_repr_css_set_property (css, "font-weight", c);
4204 font->Attribute("style", c, 256);
4205 sp_repr_css_set_property (css, "font-style", c);
4207 font->Attribute("stretch", c, 256);
4208 sp_repr_css_set_property (css, "font-stretch", c);
4210 font->Attribute("variant", c, 256);
4211 sp_repr_css_set_property (css, "font-variant", c);
4213 font->Unref();
4214 }
4215 }
4216 }
4218 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4219 if (result_fontspec == QUERY_STYLE_NOTHING)
4220 {
4221 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4222 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4223 }
4224 else
4225 {
4226 sp_desktop_set_style (desktop, css, true, true);
4227 }
4229 sp_style_unref(query);
4231 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4232 _("Text: Change font family"));
4233 sp_repr_css_attr_unref (css);
4234 free (family);
4235 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4237 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4238 }
4240 void
4241 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
4242 GObject *tbl)
4243 {
4244 const char *family = gtk_entry_get_text (entry);
4246 try {
4247 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4248 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4249 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4250 gtk_tree_selection_select_path (selection, path.gobj());
4251 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4252 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4253 } catch (...) {
4254 if (family && strlen (family))
4255 {
4256 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4257 }
4258 }
4259 }
4261 void
4262 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
4263 gpointer data)
4264 {
4265 if (g_object_get_data (G_OBJECT (button), "block")) return;
4266 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4267 int prop = GPOINTER_TO_INT(data);
4269 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4270 SPCSSAttr *css = sp_repr_css_attr_new ();
4272 switch (prop)
4273 {
4274 case 0:
4275 {
4276 sp_repr_css_set_property (css, "text-anchor", "start");
4277 sp_repr_css_set_property (css, "text-align", "start");
4278 break;
4279 }
4280 case 1:
4281 {
4282 sp_repr_css_set_property (css, "text-anchor", "middle");
4283 sp_repr_css_set_property (css, "text-align", "center");
4284 break;
4285 }
4287 case 2:
4288 {
4289 sp_repr_css_set_property (css, "text-anchor", "end");
4290 sp_repr_css_set_property (css, "text-align", "end");
4291 break;
4292 }
4294 case 3:
4295 {
4296 sp_repr_css_set_property (css, "text-anchor", "start");
4297 sp_repr_css_set_property (css, "text-align", "justify");
4298 break;
4299 }
4300 }
4302 SPStyle *query =
4303 sp_style_new (SP_ACTIVE_DOCUMENT);
4304 int result_numbers =
4305 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4307 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4308 if (result_numbers == QUERY_STYLE_NOTHING)
4309 {
4310 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4311 }
4313 sp_style_unref(query);
4315 sp_desktop_set_style (desktop, css, true, true);
4316 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4317 _("Text: Change alignment"));
4318 sp_repr_css_attr_unref (css);
4320 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4321 }
4323 void
4324 sp_text_toolbox_style_toggled (GtkToggleButton *button,
4325 gpointer data)
4326 {
4327 if (g_object_get_data (G_OBJECT (button), "block")) return;
4329 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4330 SPCSSAttr *css = sp_repr_css_attr_new ();
4331 int prop = GPOINTER_TO_INT(data);
4332 bool active = gtk_toggle_button_get_active (button);
4334 SPStyle *query =
4335 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 int result_family =
4341 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4343 int result_style =
4344 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4346 int result_numbers =
4347 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4349 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4350 Glib::ustring newFontSpec = "";
4352 if (fontSpec.empty()) {
4353 // Construct a new font specification if it does not yet exist
4354 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4355 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4356 fontFromStyle->Unref();
4357 }
4359 switch (prop)
4360 {
4361 case 0:
4362 {
4363 if (!fontSpec.empty()) {
4364 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4365 }
4366 if (fontSpec != newFontSpec) {
4367 // Don't even set the bold if the font didn't exist on the system
4368 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4369 }
4370 break;
4371 }
4373 case 1:
4374 {
4375 if (!fontSpec.empty()) {
4376 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4377 }
4378 if (fontSpec != newFontSpec) {
4379 // Don't even set the italic if the font didn't exist on the system
4380 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4381 }
4382 break;
4383 }
4384 }
4386 if (!newFontSpec.empty()) {
4387 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4388 }
4390 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4391 if (result_fontspec == QUERY_STYLE_NOTHING)
4392 {
4393 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4394 }
4396 sp_style_unref(query);
4398 sp_desktop_set_style (desktop, css, true, true);
4399 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4400 _("Text: Change font style"));
4401 sp_repr_css_attr_unref (css);
4403 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4404 }
4406 void
4407 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
4408 gpointer data)
4409 {
4410 if (g_object_get_data (G_OBJECT (button), "block")) {
4411 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4412 return;
4413 }
4415 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4416 SPCSSAttr *css = sp_repr_css_attr_new ();
4417 int prop = GPOINTER_TO_INT(data);
4419 switch (prop)
4420 {
4421 case 0:
4422 {
4423 sp_repr_css_set_property (css, "writing-mode", "lr");
4424 break;
4425 }
4427 case 1:
4428 {
4429 sp_repr_css_set_property (css, "writing-mode", "tb");
4430 break;
4431 }
4432 }
4434 SPStyle *query =
4435 sp_style_new (SP_ACTIVE_DOCUMENT);
4436 int result_numbers =
4437 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4439 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4440 if (result_numbers == QUERY_STYLE_NOTHING)
4441 {
4442 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4443 }
4445 sp_desktop_set_style (desktop, css, true, true);
4446 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4447 _("Text: Change orientation"));
4448 sp_repr_css_attr_unref (css);
4450 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4451 }
4453 gboolean
4454 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, gpointer /*data*/)
4455 {
4456 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4457 if (!desktop) return FALSE;
4459 switch (get_group0_keyval (event)) {
4460 case GDK_Escape: // defocus
4461 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4462 return TRUE; // I consumed the event
4463 break;
4464 case GDK_Return: // defocus
4465 case GDK_KP_Enter:
4466 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4467 return TRUE; // I consumed the event
4468 break;
4469 }
4470 return FALSE;
4471 }
4473 gboolean
4474 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4475 {
4476 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4477 if (!desktop) return FALSE;
4479 switch (get_group0_keyval (event)) {
4480 case GDK_Escape: // defocus
4481 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4482 sp_text_toolbox_selection_changed (NULL, tbl); // update
4483 return TRUE; // I consumed the event
4484 break;
4485 }
4486 return FALSE;
4487 }
4489 gboolean
4490 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4491 {
4492 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4493 if (!desktop) return FALSE;
4495 switch (get_group0_keyval (event)) {
4496 case GDK_KP_Enter:
4497 case GDK_Return:
4498 case GDK_Escape: // defocus
4499 gtk_widget_hide (w);
4500 visible = false;
4501 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4502 return TRUE; // I consumed the event
4503 break;
4504 }
4505 return FALSE;
4506 }
4509 void
4510 sp_text_toolbox_size_changed (GtkComboBox *cbox,
4511 GObject *tbl)
4512 {
4513 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4515 if (g_object_get_data (tbl, "size-block")) return;
4517 char *text = gtk_combo_box_get_active_text (cbox);
4519 SPCSSAttr *css = sp_repr_css_attr_new ();
4520 sp_repr_css_set_property (css, "font-size", text);
4521 free (text);
4523 SPStyle *query =
4524 sp_style_new (SP_ACTIVE_DOCUMENT);
4525 int result_numbers =
4526 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4528 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4529 if (result_numbers == QUERY_STYLE_NOTHING)
4530 {
4531 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4532 }
4534 sp_style_unref(query);
4536 sp_desktop_set_style (desktop, css, true, true);
4537 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4538 _("Text: Change font size"));
4539 sp_repr_css_attr_unref (css);
4542 if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
4543 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4544 }
4546 void
4547 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
4548 GObject *tbl)
4549 {
4550 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4551 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4552 int x, y;
4554 if (!visible)
4555 {
4556 gdk_window_get_origin (widget->window, &x, &y);
4557 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4558 gtk_widget_show_all (popdown);
4560 gdk_pointer_grab (widget->window, TRUE,
4561 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4562 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4563 GDK_POINTER_MOTION_MASK),
4564 NULL, NULL, GDK_CURRENT_TIME);
4566 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4568 visible = true;
4569 }
4570 else
4571 {
4572 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4573 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4574 gtk_widget_hide (popdown);
4575 visible = false;
4576 }
4577 }
4579 gboolean
4580 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
4581 GdkEventFocus */*event*/,
4582 GObject */*tbl*/)
4583 {
4584 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4585 return FALSE;
4586 }
4588 gboolean
4589 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
4590 GdkEventFocus */*event*/,
4591 GObject */*tbl*/)
4592 {
4593 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4595 gtk_widget_hide (popdown);
4596 visible = false;
4597 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4598 return TRUE;
4599 }
4601 void
4602 cell_data_func (GtkTreeViewColumn */*column*/,
4603 GtkCellRenderer *cell,
4604 GtkTreeModel *tree_model,
4605 GtkTreeIter *iter,
4606 gpointer /*data*/)
4607 {
4608 char *family,
4609 *family_escaped,
4610 *sample_escaped;
4612 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4614 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4616 family_escaped = g_markup_escape_text (family, -1);
4617 sample_escaped = g_markup_escape_text (sample, -1);
4619 std::stringstream markup;
4620 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4621 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4623 free (family);
4624 free (family_escaped);
4625 free (sample_escaped);
4626 }
4628 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4629 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4630 if (completion) {
4631 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4632 g_object_unref (completion);
4633 }
4634 }
4636 GtkWidget*
4637 sp_text_toolbox_new (SPDesktop *desktop)
4638 {
4639 GtkWidget *tbl = gtk_hbox_new (FALSE, 0);
4641 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4642 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4644 GtkTooltips *tt = gtk_tooltips_new();
4645 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4647 ////////////Family
4648 //Window
4649 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4650 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4652 //Entry
4653 GtkWidget *entry = gtk_entry_new ();
4654 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4655 GtkEntryCompletion *completion = gtk_entry_completion_new ();
4656 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4657 gtk_entry_completion_set_text_column (completion, 0);
4658 gtk_entry_completion_set_minimum_key_length (completion, 1);
4659 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4660 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4661 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4662 aux_toolbox_space (tbl, 1);
4663 gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4664 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4666 //Button
4667 GtkWidget *button = gtk_button_new ();
4668 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4669 gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4671 //Popdown
4672 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
4673 GtkWidget *treeview = gtk_tree_view_new ();
4675 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
4676 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
4677 gtk_tree_view_column_pack_start (column, cell, FALSE);
4678 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4679 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4680 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4682 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4683 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4684 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4686 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4688 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4689 gtk_container_add (GTK_CONTAINER (sw), treeview);
4691 gtk_container_add (GTK_CONTAINER (window), sw);
4692 gtk_widget_set_size_request (window, 300, 450);
4694 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4695 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4696 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4698 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4700 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4701 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4703 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4704 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4706 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4707 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4708 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4709 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4710 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4712 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4713 aux_toolbox_space (tbl, 1);
4714 GtkWidget *box = gtk_event_box_new ();
4715 gtk_container_add (GTK_CONTAINER (box), image);
4716 gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4717 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4718 GtkTooltips *tooltips = gtk_tooltips_new ();
4719 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4720 gtk_widget_hide (GTK_WIDGET (box));
4721 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4723 ////////////Size
4724 const char *sizes[] = {
4725 "4", "6", "8", "9", "10", "11", "12", "13", "14",
4726 "16", "18", "20", "22", "24", "28",
4727 "32", "36", "40", "48", "56", "64", "72", "144"
4728 };
4730 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4731 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4732 gtk_widget_set_size_request (cbox, 80, -1);
4733 aux_toolbox_space (tbl, 1);
4734 gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4735 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4736 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4737 gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
4739 //spacer
4740 aux_toolbox_space (tbl, 4);
4741 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4743 ////////////Text anchor
4744 GtkWidget *group = gtk_radio_button_new (NULL);
4745 GtkWidget *row = gtk_hbox_new (FALSE, 4);
4746 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4748 // left
4749 GtkWidget *rbutton = group;
4750 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4751 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4752 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4754 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4755 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
4756 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4757 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4759 // center
4760 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4761 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4762 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4763 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4765 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4766 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
4767 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4768 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4770 // right
4771 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4772 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4773 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4774 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4776 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4777 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
4778 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4779 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4781 // fill
4782 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4783 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4784 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4785 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4787 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4788 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
4789 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4790 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4792 aux_toolbox_space (tbl, 1);
4793 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4795 //spacer
4796 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4798 ////////////Text style
4799 row = gtk_hbox_new (FALSE, 4);
4801 // bold
4802 rbutton = gtk_toggle_button_new ();
4803 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4804 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4805 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4806 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4808 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4809 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
4810 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4812 // italic
4813 rbutton = gtk_toggle_button_new ();
4814 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4815 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4816 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4817 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4819 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4820 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
4821 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4823 aux_toolbox_space (tbl, 1);
4824 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4826 //spacer
4827 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4829 ////////////Text orientation
4830 group = gtk_radio_button_new (NULL);
4831 row = gtk_hbox_new (FALSE, 4);
4832 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4834 // horizontal
4835 rbutton = group;
4836 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4837 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4838 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4839 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4841 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4842 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4843 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4845 // vertical
4846 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4847 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4848 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4849 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4850 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4852 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4853 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
4854 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4855 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4858 //watch selection
4859 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4861 sigc::connection *c_selection_changed =
4862 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4863 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4864 pool->add_connection ("selection-changed", c_selection_changed);
4866 sigc::connection *c_selection_modified =
4867 new sigc::connection (sp_desktop_selection (desktop)->connectModified
4868 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4869 pool->add_connection ("selection-modified", c_selection_modified);
4871 sigc::connection *c_subselection_changed =
4872 new sigc::connection (desktop->connectToolSubselectionChanged
4873 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4874 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4876 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4879 gtk_widget_show_all (tbl);
4880 return tbl;
4882 } // end of sp_text_toolbox_new()
4884 }//<unnamed> namespace
4887 //#########################
4888 //## Connector ##
4889 //#########################
4891 static void sp_connector_path_set_avoid(void)
4892 {
4893 cc_selection_set_avoid(true);
4894 }
4897 static void sp_connector_path_set_ignore(void)
4898 {
4899 cc_selection_set_avoid(false);
4900 }
4904 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4905 {
4906 // quit if run by the _changed callbacks
4907 if (g_object_get_data( tbl, "freeze" )) {
4908 return;
4909 }
4911 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4912 SPDocument *doc = sp_desktop_document(desktop);
4914 if (!sp_document_get_undo_sensitive(doc))
4915 {
4916 return;
4917 }
4919 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4921 if ( repr->attribute("inkscape:connector-spacing") ) {
4922 gdouble priorValue = gtk_adjustment_get_value(adj);
4923 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4924 if ( priorValue == gtk_adjustment_get_value(adj) ) {
4925 return;
4926 }
4927 } else if ( adj->value == defaultConnSpacing ) {
4928 return;
4929 }
4931 // in turn, prevent callbacks from responding
4932 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4934 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4935 SP_OBJECT(desktop->namedview)->updateRepr();
4937 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4938 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4939 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4940 NR::Matrix m = NR::identity();
4941 avoid_item_move(&m, item);
4942 }
4944 if (items) {
4945 g_slist_free(items);
4946 }
4948 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4949 _("Change connector spacing"));
4951 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4953 spinbutton_defocus(GTK_OBJECT(tbl));
4954 }
4956 static void sp_connector_graph_layout(void)
4957 {
4958 if (!SP_ACTIVE_DESKTOP) return;
4960 // hack for clones, see comment in align-and-distribute.cpp
4961 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4962 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
4964 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
4966 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
4968 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
4969 }
4971 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4972 {
4973 if ( gtk_toggle_action_get_active( act ) ) {
4974 prefs_set_string_attribute("tools.connector", "directedlayout",
4975 "true");
4976 } else {
4977 prefs_set_string_attribute("tools.connector", "directedlayout",
4978 "false");
4979 }
4980 }
4982 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
4983 {
4984 if ( gtk_toggle_action_get_active( act ) ) {
4985 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4986 "true");
4987 } else {
4988 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
4989 "false");
4990 }
4991 }
4994 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
4995 {
4996 prefs_set_double_attribute("tools.connector", "length", adj->value);
4997 spinbutton_defocus(GTK_OBJECT(tbl));
4998 }
5000 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5001 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5002 bool /*is_interactive*/, gpointer data)
5003 {
5004 GtkWidget *tbl = GTK_WIDGET(data);
5006 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5007 return;
5008 }
5009 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5010 return;
5011 }
5013 GtkAdjustment *adj = (GtkAdjustment*)
5014 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5015 gdouble spacing = defaultConnSpacing;
5016 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5018 gtk_adjustment_set_value(adj, spacing);
5019 }
5022 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5023 NULL, /* child_added */
5024 NULL, /* child_removed */
5025 connector_tb_event_attr_changed,
5026 NULL, /* content_changed */
5027 NULL /* order_changed */
5028 };
5031 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5032 {
5033 {
5034 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5035 _("Avoid"),
5036 _("Make connectors avoid selected objects"),
5037 "connector_avoid",
5038 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5039 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5040 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5041 }
5043 {
5044 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5045 _("Ignore"),
5046 _("Make connectors ignore selected objects"),
5047 "connector_ignore",
5048 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5049 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5050 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5051 }
5053 EgeAdjustmentAction* eact = 0;
5055 // Spacing spinbox
5056 eact = create_adjustment_action( "ConnectorSpacingAction",
5057 _("Connector Spacing"), _("Spacing:"),
5058 _("The amount of space left around objects by auto-routing connectors"),
5059 "tools.connector", "spacing", defaultConnSpacing,
5060 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5061 0, 100, 1.0, 10.0,
5062 0, 0, 0,
5063 connector_spacing_changed, 1, 0 );
5064 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5066 // Graph (connector network) layout
5067 {
5068 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5069 _("Graph"),
5070 _("Nicely arrange selected connector network"),
5071 "graph_layout",
5072 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5073 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5074 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5075 }
5077 // Default connector length spinbox
5078 eact = create_adjustment_action( "ConnectorLengthAction",
5079 _("Connector Length"), _("Length:"),
5080 _("Ideal length for connectors when layout is applied"),
5081 "tools.connector", "length", 100,
5082 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5083 10, 1000, 10.0, 100.0,
5084 0, 0, 0,
5085 connector_length_changed, 1, 0 );
5086 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5089 // Directed edges toggle button
5090 {
5091 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5092 _("Downwards"),
5093 _("Make connectors with end-markers (arrows) point downwards"),
5094 "directed_graph",
5095 Inkscape::ICON_SIZE_DECORATION );
5096 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5098 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5099 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5100 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5102 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5103 }
5105 // Avoid overlaps toggle button
5106 {
5107 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5108 _("Remove overlaps"),
5109 _("Do not allow overlapping shapes"),
5110 "remove_overlaps",
5111 Inkscape::ICON_SIZE_DECORATION );
5112 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5114 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5115 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5116 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5118 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5119 }
5121 // Code to watch for changes to the connector-spacing attribute in
5122 // the XML.
5123 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5124 g_assert(repr != NULL);
5126 purge_repr_listener( holder, holder );
5128 if (repr) {
5129 g_object_set_data( holder, "repr", repr );
5130 Inkscape::GC::anchor(repr);
5131 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5132 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5133 }
5134 } // end of sp_connector_toolbox_prep()
5137 //#########################
5138 //## Paintbucket ##
5139 //#########################
5141 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5142 {
5143 gint channels = ege_select_one_action_get_active( act );
5144 flood_channels_set_channels( channels );
5145 }
5147 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5148 {
5149 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5150 }
5152 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5153 {
5154 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5155 }
5157 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5158 {
5159 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5160 SPUnit const *unit = tracker->getActiveUnit();
5162 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5164 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5165 }
5167 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5168 {
5169 // FIXME: make defaults settable via Inkscape Options
5170 struct KeyValue {
5171 char const *key;
5172 double value;
5173 } const key_values[] = {
5174 {"threshold", 15},
5175 {"offset", 0.0}
5176 };
5178 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5179 KeyValue const &kv = key_values[i];
5180 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5181 if ( adj ) {
5182 gtk_adjustment_set_value(adj, kv.value);
5183 }
5184 }
5186 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5187 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5188 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5189 ege_select_one_action_set_active( autogap_action, 0 );
5190 }
5192 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5193 {
5194 EgeAdjustmentAction* eact = 0;
5196 {
5197 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5199 GList* items = 0;
5200 gint count = 0;
5201 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5202 {
5203 GtkTreeIter iter;
5204 gtk_list_store_append( model, &iter );
5205 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5206 count++;
5207 }
5208 g_list_free( items );
5209 items = 0;
5210 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5211 g_object_set( act1, "short_label", _("Fill by:"), NULL );
5212 ege_select_one_action_set_appearance( act1, "compact" );
5213 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5214 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5215 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5216 g_object_set_data( holder, "channels_action", act1 );
5217 }
5219 // Spacing spinbox
5220 {
5221 eact = create_adjustment_action(
5222 "ThresholdAction",
5223 _("Fill Threshold"), _("Threshold:"),
5224 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5225 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5226 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5227 0, 0, 0,
5228 paintbucket_threshold_changed, 1, 0 );
5230 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5231 }
5233 // Create the units menu.
5234 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5235 tracker->setActiveUnit(sp_unit_get_by_abbreviation(prefs_get_string_attribute("tools.paintbucket", "offsetunits")));
5236 g_object_set_data( holder, "tracker", tracker );
5237 {
5238 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5239 gtk_action_group_add_action( mainActions, act );
5240 }
5242 // Offset spinbox
5243 {
5244 eact = create_adjustment_action(
5245 "OffsetAction",
5246 _("Grow/shrink by"), _("Grow/shrink by:"),
5247 _("The amount to grow (positive) or shrink (negative) the created fill path"),
5248 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5249 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5250 0, 0, 0,
5251 paintbucket_offset_changed, 1, 2);
5252 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5254 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5255 }
5257 /* Auto Gap */
5258 {
5259 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5261 GList* items = 0;
5262 gint count = 0;
5263 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5264 {
5265 GtkTreeIter iter;
5266 gtk_list_store_append( model, &iter );
5267 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5268 count++;
5269 }
5270 g_list_free( items );
5271 items = 0;
5272 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5273 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5274 ege_select_one_action_set_appearance( act2, "compact" );
5275 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5276 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5277 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5278 g_object_set_data( holder, "autogap_action", act2 );
5279 }
5281 /* Reset */
5282 {
5283 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5284 _("Defaults"),
5285 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5286 GTK_STOCK_CLEAR );
5287 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5288 gtk_action_group_add_action( mainActions, act );
5289 gtk_action_set_sensitive( act, TRUE );
5290 }
5292 }
5294 /*
5295 Local Variables:
5296 mode:c++
5297 c-file-style:"stroustrup"
5298 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5299 indent-tabs-mode:nil
5300 fill-column:99
5301 End:
5302 */
5303 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :