8cdf0a07fcee4869fb4a1fe4cddb09f19670a4cc
1 /** \file
2 * Controls bars for some of Inkscape's tools
3 * (for some tools, they are in their own files)
4 */
6 /*
7 *
8 * Authors:
9 * MenTaLguY <mental@rydia.net>
10 * Lauris Kaplinski <lauris@kaplinski.com>
11 * bulia byak <buliabyak@users.sf.net>
12 * Frank Felfe <innerspace@iname.com>
13 * John Cliff <simarilius@yahoo.com>
14 * David Turner <novalis@gnu.org>
15 * Josh Andler <scislac@scislac.com>
16 * Jon A. Cruz <jon@joncruz.org>
17 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2006 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <cstring>
31 #include <string>
33 #include <gtkmm.h>
34 #include <gtk/gtk.h>
35 #include <iostream>
36 #include <sstream>
38 #include "widgets/button.h"
39 #include "widgets/widget-sizes.h"
40 #include "widgets/spw-utilities.h"
41 #include "widgets/spinbutton-events.h"
42 #include "dialogs/text-edit.h"
43 #include "dialogs/dialog-events.h"
45 #include "ui/widget/style-swatch.h"
47 #include "prefs-utils.h"
48 #include "verbs.h"
49 #include "sp-namedview.h"
50 #include "desktop.h"
51 #include "desktop-handles.h"
52 #include "xml/repr.h"
53 #include "xml/node-event-vector.h"
54 #include <glibmm/i18n.h>
55 #include "helper/unit-menu.h"
56 #include "helper/units.h"
58 #include "inkscape.h"
59 #include "conn-avoid-ref.h"
62 #include "select-toolbar.h"
63 #include "gradient-toolbar.h"
65 #include "connector-context.h"
66 #include "node-context.h"
67 #include "shape-editor.h"
68 #include "tweak-context.h"
69 #include "sp-rect.h"
70 #include "box3d.h"
71 #include "box3d-context.h"
72 #include "sp-star.h"
73 #include "sp-spiral.h"
74 #include "sp-ellipse.h"
75 #include "sp-text.h"
76 #include "sp-flowtext.h"
77 #include "style.h"
78 #include "selection.h"
79 #include "selection-chemistry.h"
80 #include "document-private.h"
81 #include "desktop-style.h"
82 #include "../libnrtype/font-lister.h"
83 #include "../libnrtype/font-instance.h"
84 #include "../connection-pool.h"
85 #include "../prefs-utils.h"
86 #include "../inkscape-stock.h"
87 #include "icon.h"
88 #include "graphlayout/graphlayout.h"
90 #include "mod360.h"
92 #include "toolbox.h"
94 #include "flood-context.h"
96 #include "ink-action.h"
97 #include "ege-adjustment-action.h"
98 #include "ege-output-action.h"
99 #include "ege-select-one-action.h"
100 #include "helper/unit-tracker.h"
102 #include "svg/css-ostringstream.h"
104 using Inkscape::UnitTracker;
106 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
107 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
109 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
122 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
128 static struct {
129 gchar const *type_name;
130 gchar const *data_name;
131 sp_verb_t verb;
132 sp_verb_t doubleclick_verb;
133 } const tools[] = {
134 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
135 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
136 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
137 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
138 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
139 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
140 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
141 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
142 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
143 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
144 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
145 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
146 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
147 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
148 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
149 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
150 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
151 { NULL, NULL, 0, 0 }
152 };
154 static struct {
155 gchar const *type_name;
156 gchar const *data_name;
157 GtkWidget *(*create_func)(SPDesktop *desktop);
158 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
159 gchar const *ui_name;
160 gint swatch_verb_id;
161 gchar const *swatch_tool;
162 gchar const *swatch_tip;
163 } const aux_toolboxes[] = {
164 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
165 SP_VERB_INVALID, 0, 0},
166 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
167 SP_VERB_INVALID, 0, 0},
168 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
169 SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
170 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
171 SP_VERB_INVALID, 0, 0},
172 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
173 SP_VERB_CONTEXT_STAR_PREFS, "tools.shapes.star", _("Style of new stars")},
174 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
175 SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", _("Style of new rectangles")},
176 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
177 SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", _("Style of new 3D boxes")},
178 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
179 SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", _("Style of new ellipses")},
180 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
181 SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral", _("Style of new spirals")},
182 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
183 SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
184 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
185 SP_VERB_CONTEXT_PEN_PREFS, "tools.freehand.pen", _("Style of new paths created by Pen")},
186 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
187 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
188 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
189 SP_VERB_INVALID, 0, 0},
190 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
191 SP_VERB_INVALID, 0, 0},
192 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
193 SP_VERB_INVALID, 0, 0},
194 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
195 SP_VERB_INVALID, 0, 0},
196 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
197 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
198 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
199 };
202 static gchar const * ui_descr =
203 "<ui>"
204 " <toolbar name='SelectToolbar'>"
205 " <toolitem action='EditSelectAll' />"
206 " <toolitem action='EditSelectAllInAllLayers' />"
207 " <toolitem action='EditDeselect' />"
208 " <separator />"
209 " <toolitem action='ObjectRotate90CCW' />"
210 " <toolitem action='ObjectRotate90' />"
211 " <toolitem action='ObjectFlipHorizontally' />"
212 " <toolitem action='ObjectFlipVertically' />"
213 " <separator />"
214 " <toolitem action='SelectionToBack' />"
215 " <toolitem action='SelectionLower' />"
216 " <toolitem action='SelectionRaise' />"
217 " <toolitem action='SelectionToFront' />"
218 " <separator />"
219 " <toolitem action='XAction' />"
220 " <toolitem action='YAction' />"
221 " <toolitem action='WidthAction' />"
222 " <toolitem action='LockAction' />"
223 " <toolitem action='HeightAction' />"
224 " <toolitem action='UnitsAction' />"
225 " <separator />"
226 " <toolitem action='transform_affect_label' />"
227 " <toolitem action='transform_stroke' />"
228 " <toolitem action='transform_corners' />"
229 " <toolitem action='transform_gradient' />"
230 " <toolitem action='transform_pattern' />"
231 " </toolbar>"
233 " <toolbar name='NodeToolbar'>"
234 " <toolitem action='NodeInsertAction' />"
235 " <toolitem action='NodeDeleteAction' />"
236 " <separator />"
237 " <toolitem action='NodeJoinAction' />"
238 " <toolitem action='NodeJoinSegmentAction' />"
239 " <toolitem action='NodeDeleteSegmentAction' />"
240 " <toolitem action='NodeBreakAction' />"
241 " <separator />"
242 " <toolitem action='NodeCuspAction' />"
243 " <toolitem action='NodeSmoothAction' />"
244 " <toolitem action='NodeSymmetricAction' />"
245 " <separator />"
246 " <toolitem action='NodeLineAction' />"
247 " <toolitem action='NodeCurveAction' />"
248 " <separator />"
249 " <toolitem action='ObjectToPath' />"
250 " <toolitem action='StrokeToPath' />"
251 " <separator />"
252 " <toolitem action='NodesShowHandlesAction' />"
253 " <separator />"
254 " <toolitem action='EditNextLPEParameterAction' />"
255 " <separator />"
256 " <toolitem action='NodeXAction' />"
257 " <toolitem action='NodeYAction' />"
258 " <toolitem action='NodeUnitsAction' />"
259 " </toolbar>"
261 " <toolbar name='TweakToolbar'>"
262 " <toolitem action='TweakWidthAction' />"
263 " <separator />"
264 " <toolitem action='TweakForceAction' />"
265 " <toolitem action='TweakPressureAction' />"
266 " <separator />"
267 " <toolitem action='TweakModeAction' />"
268 " <separator />"
269 " <toolitem action='TweakFidelityAction' />"
270 " <separator />"
271 " <toolitem action='TweakChannelsLabel' />"
272 " <toolitem action='TweakDoH' />"
273 " <toolitem action='TweakDoS' />"
274 " <toolitem action='TweakDoL' />"
275 " <toolitem action='TweakDoO' />"
276 " </toolbar>"
278 " <toolbar name='ZoomToolbar'>"
279 " <toolitem action='ZoomIn' />"
280 " <toolitem action='ZoomOut' />"
281 " <separator />"
282 " <toolitem action='Zoom1:0' />"
283 " <toolitem action='Zoom1:2' />"
284 " <toolitem action='Zoom2:1' />"
285 " <separator />"
286 " <toolitem action='ZoomSelection' />"
287 " <toolitem action='ZoomDrawing' />"
288 " <toolitem action='ZoomPage' />"
289 " <toolitem action='ZoomPageWidth' />"
290 " <separator />"
291 " <toolitem action='ZoomPrev' />"
292 " <toolitem action='ZoomNext' />"
293 " </toolbar>"
295 " <toolbar name='StarToolbar'>"
296 " <separator />"
297 " <toolitem action='StarStateAction' />"
298 " <separator />"
299 " <toolitem action='FlatAction' />"
300 " <separator />"
301 " <toolitem action='MagnitudeAction' />"
302 " <toolitem action='SpokeAction' />"
303 " <toolitem action='RoundednessAction' />"
304 " <toolitem action='RandomizationAction' />"
305 " <separator />"
306 " <toolitem action='StarResetAction' />"
307 " </toolbar>"
309 " <toolbar name='RectToolbar'>"
310 " <toolitem action='RectStateAction' />"
311 " <toolitem action='RectWidthAction' />"
312 " <toolitem action='RectHeightAction' />"
313 " <toolitem action='RadiusXAction' />"
314 " <toolitem action='RadiusYAction' />"
315 " <toolitem action='RectUnitsAction' />"
316 " <separator />"
317 " <toolitem action='RectResetAction' />"
318 " </toolbar>"
320 " <toolbar name='3DBoxToolbar'>"
321 " <toolitem action='3DBoxAngleXAction' />"
322 " <toolitem action='3DBoxVPXStateAction' />"
323 " <separator />"
324 " <toolitem action='3DBoxAngleYAction' />"
325 " <toolitem action='3DBoxVPYStateAction' />"
326 " <separator />"
327 " <toolitem action='3DBoxAngleZAction' />"
328 " <toolitem action='3DBoxVPZStateAction' />"
329 " </toolbar>"
331 " <toolbar name='SpiralToolbar'>"
332 " <toolitem action='SpiralStateAction' />"
333 " <toolitem action='SpiralRevolutionAction' />"
334 " <toolitem action='SpiralExpansionAction' />"
335 " <toolitem action='SpiralT0Action' />"
336 " <separator />"
337 " <toolitem action='SpiralResetAction' />"
338 " </toolbar>"
340 " <toolbar name='PenToolbar'>"
341 " </toolbar>"
343 " <toolbar name='PencilToolbar'>"
344 " </toolbar>"
346 " <toolbar name='CalligraphyToolbar'>"
347 " <separator />"
348 " <toolitem action='CalligraphyWidthAction' />"
349 " <toolitem action='PressureAction' />"
350 " <toolitem action='TraceAction' />"
351 " <toolitem action='ThinningAction' />"
352 " <separator />"
353 " <toolitem action='AngleAction' />"
354 " <toolitem action='TiltAction' />"
355 " <toolitem action='FixationAction' />"
356 " <separator />"
357 " <toolitem action='CapRoundingAction' />"
358 " <separator />"
359 " <toolitem action='TremorAction' />"
360 " <toolitem action='WiggleAction' />"
361 " <toolitem action='MassAction' />"
362 " <separator />"
363 " <toolitem action='CalligraphyResetAction' />"
364 " </toolbar>"
366 " <toolbar name='ArcToolbar'>"
367 " <toolitem action='ArcStateAction' />"
368 " <separator />"
369 " <toolitem action='ArcStartAction' />"
370 " <toolitem action='ArcEndAction' />"
371 " <separator />"
372 " <toolitem action='ArcOpenAction' />"
373 " <separator />"
374 " <toolitem action='ArcResetAction' />"
375 " <separator />"
376 " </toolbar>"
378 " <toolbar name='PaintbucketToolbar'>"
379 " <toolitem action='ChannelsAction' />"
380 " <separator />"
381 " <toolitem action='ThresholdAction' />"
382 " <separator />"
383 " <toolitem action='OffsetAction' />"
384 " <toolitem action='PaintbucketUnitsAction' />"
385 " <separator />"
386 " <toolitem action='AutoGapAction' />"
387 " <separator />"
388 " <toolitem action='PaintbucketResetAction' />"
389 " </toolbar>"
391 " <toolbar name='DropperToolbar'>"
392 " <toolitem action='DropperPickAlphaAction' />"
393 " <toolitem action='DropperSetAlphaAction' />"
394 " </toolbar>"
396 " <toolbar name='ConnectorToolbar'>"
397 " <toolitem action='ConnectorAvoidAction' />"
398 " <toolitem action='ConnectorIgnoreAction' />"
399 " <toolitem action='ConnectorSpacingAction' />"
400 " <toolitem action='ConnectorGraphAction' />"
401 " <toolitem action='ConnectorLengthAction' />"
402 " <toolitem action='ConnectorDirectedAction' />"
403 " <toolitem action='ConnectorOverlapAction' />"
404 " </toolbar>"
406 "</ui>"
407 ;
409 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
411 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
413 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
414 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
416 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
417 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
419 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
420 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
422 /* Global text entry widgets necessary for update */
423 /* GtkWidget *dropper_rgb_entry,
424 *dropper_opacity_entry ; */
425 // should be made a private member once this is converted to class
427 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
428 connection->disconnect();
429 delete connection;
430 }
432 static void purge_repr_listener( GObject* obj, GObject* tbl )
433 {
434 (void)obj;
435 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
436 if (oldrepr) { // remove old listener
437 sp_repr_remove_listener_by_data(oldrepr, tbl);
438 Inkscape::GC::release(oldrepr);
439 oldrepr = 0;
440 g_object_set_data( tbl, "repr", NULL );
441 }
442 }
444 GtkWidget *
445 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
446 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
447 Inkscape::UI::View::View *view, GtkTooltips *tt)
448 {
449 SPAction *action = verb->get_action(view);
450 if (!action) return NULL;
452 SPAction *doubleclick_action;
453 if (doubleclick_verb)
454 doubleclick_action = doubleclick_verb->get_action(view);
455 else
456 doubleclick_action = NULL;
458 /* fixme: Handle sensitive/unsensitive */
459 /* fixme: Implement sp_button_new_from_action */
460 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
461 gtk_widget_show(b);
462 gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
464 return b;
465 }
467 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
468 Inkscape::UI::View::View *view, GtkTooltips *tt)
469 {
470 return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
471 }
473 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
474 Inkscape::UI::View::View *view, GtkTooltips *tt)
475 {
476 return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
477 }
480 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
481 {
482 SPAction* targetAction = SP_ACTION(user_data);
483 if ( targetAction ) {
484 sp_action_perform( targetAction, NULL );
485 }
486 }
488 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
489 {
490 if ( data ) {
491 GtkAction* act = GTK_ACTION(data);
492 gtk_action_set_sensitive( act, sensitive );
493 }
494 }
496 static SPActionEventVector action_event_vector = {
497 {NULL},
498 NULL,
499 NULL,
500 sp_action_action_set_sensitive,
501 NULL,
502 NULL
503 };
505 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
506 {
507 GtkAction* act = 0;
509 SPAction* targetAction = verb->get_action(view);
510 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
511 act = GTK_ACTION(inky);
512 gtk_action_set_sensitive( act, targetAction->sensitive );
514 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
516 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
517 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
519 return act;
520 }
522 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
523 {
524 Inkscape::UI::View::View *view = desktop;
525 gint verbsToUse[] = {
526 // disabled until we have icons for them:
527 //find
528 //SP_VERB_EDIT_TILE,
529 //SP_VERB_EDIT_UNTILE,
530 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
531 SP_VERB_DIALOG_DISPLAY,
532 SP_VERB_DIALOG_FILL_STROKE,
533 SP_VERB_DIALOG_NAMEDVIEW,
534 SP_VERB_DIALOG_TEXT,
535 SP_VERB_DIALOG_XML_EDITOR,
536 SP_VERB_EDIT_CLONE,
537 SP_VERB_EDIT_COPY,
538 SP_VERB_EDIT_CUT,
539 SP_VERB_EDIT_DUPLICATE,
540 SP_VERB_EDIT_PASTE,
541 SP_VERB_EDIT_REDO,
542 SP_VERB_EDIT_UNDO,
543 SP_VERB_EDIT_UNLINK_CLONE,
544 SP_VERB_FILE_EXPORT,
545 SP_VERB_FILE_IMPORT,
546 SP_VERB_FILE_NEW,
547 SP_VERB_FILE_OPEN,
548 SP_VERB_FILE_PRINT,
549 SP_VERB_FILE_SAVE,
550 SP_VERB_OBJECT_TO_CURVE,
551 SP_VERB_SELECTION_GROUP,
552 SP_VERB_SELECTION_OUTLINE,
553 SP_VERB_SELECTION_UNGROUP,
554 SP_VERB_ZOOM_1_1,
555 SP_VERB_ZOOM_1_2,
556 SP_VERB_ZOOM_2_1,
557 SP_VERB_ZOOM_DRAWING,
558 SP_VERB_ZOOM_IN,
559 SP_VERB_ZOOM_NEXT,
560 SP_VERB_ZOOM_OUT,
561 SP_VERB_ZOOM_PAGE,
562 SP_VERB_ZOOM_PAGE_WIDTH,
563 SP_VERB_ZOOM_PREV,
564 SP_VERB_ZOOM_SELECTION,
565 };
567 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
568 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
570 static std::map<SPDesktop*, GtkActionGroup*> groups;
571 GtkActionGroup* mainActions = 0;
572 if ( groups.find(desktop) != groups.end() ) {
573 mainActions = groups[desktop];
574 }
576 if ( !mainActions ) {
577 mainActions = gtk_action_group_new("main");
578 groups[desktop] = mainActions;
579 }
581 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
582 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
583 if ( verb ) {
584 if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
585 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
586 gtk_action_group_add_action( mainActions, act );
587 }
588 }
589 }
591 return mainActions;
592 }
595 GtkWidget *
596 sp_tool_toolbox_new()
597 {
598 GtkTooltips *tt = gtk_tooltips_new();
599 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
601 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
602 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
604 gtk_widget_set_sensitive(tb, FALSE);
606 GtkWidget *hb = gtk_handle_box_new();
607 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
608 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
609 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
611 gtk_container_add(GTK_CONTAINER(hb), tb);
612 gtk_widget_show(GTK_WIDGET(tb));
614 sigc::connection* conn = new sigc::connection;
615 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
617 return hb;
618 }
620 static void
621 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
622 {
623 g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
624 gtk_widget_queue_resize(child);
625 }
627 static void
628 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
629 {
630 g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
631 gtk_widget_queue_resize(child);
632 }
634 GtkWidget *
635 sp_aux_toolbox_new()
636 {
637 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
639 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
641 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
643 gtk_widget_set_sensitive(tb, FALSE);
645 GtkWidget *hb = gtk_handle_box_new();
646 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
647 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
648 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
650 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
651 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
653 gtk_container_add(GTK_CONTAINER(hb), tb);
654 gtk_widget_show(GTK_WIDGET(tb));
656 sigc::connection* conn = new sigc::connection;
657 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
659 return hb;
660 }
662 //####################################
663 //# Commands Bar
664 //####################################
666 GtkWidget *
667 sp_commands_toolbox_new()
668 {
669 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
671 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
673 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
674 gtk_widget_set_sensitive(tb, FALSE);
676 GtkWidget *hb = gtk_handle_box_new();
677 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
678 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
679 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
681 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
682 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
684 gtk_container_add(GTK_CONTAINER(hb), tb);
685 gtk_widget_show(GTK_WIDGET(tb));
687 sigc::connection* conn = new sigc::connection;
688 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
690 return hb;
691 }
693 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
694 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
695 gchar const *path, gchar const *data, gdouble def,
696 GtkWidget *focusTarget,
697 GtkWidget *us,
698 GObject *dataKludge,
699 gboolean altx, gchar const *altx_mark,
700 gdouble lower, gdouble upper, gdouble step, gdouble page,
701 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
702 void (*callback)(GtkAdjustment *, GObject *),
703 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
704 {
705 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
706 lower, upper, step, page, page ) );
707 if (us) {
708 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
709 }
711 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
713 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
714 if ( shortLabel ) {
715 g_object_set( act, "short_label", shortLabel, NULL );
716 }
718 if ( (descrCount > 0) && descrLabels && descrValues ) {
719 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
720 }
722 if ( focusTarget ) {
723 ege_adjustment_action_set_focuswidget( act, focusTarget );
724 }
726 if ( altx && altx_mark ) {
727 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
728 }
730 if ( dataKludge ) {
731 g_object_set_data( dataKludge, data, adj );
732 }
734 // Using a cast just to make sure we pass in the right kind of function pointer
735 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
737 return act;
738 }
741 //####################################
742 //# node editing callbacks
743 //####################################
745 /**
746 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
747 */
748 static ShapeEditor *get_current_shape_editor()
749 {
750 if (!SP_ACTIVE_DESKTOP) {
751 return NULL;
752 }
754 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
756 if (!SP_IS_NODE_CONTEXT(event_context)) {
757 return NULL;
758 }
760 return SP_NODE_CONTEXT(event_context)->shape_editor;
761 }
764 void
765 sp_node_path_edit_add(void)
766 {
767 ShapeEditor *shape_editor = get_current_shape_editor();
768 if (shape_editor) shape_editor->add_node();
769 }
771 void
772 sp_node_path_edit_delete(void)
773 {
774 ShapeEditor *shape_editor = get_current_shape_editor();
775 if (shape_editor) shape_editor->delete_nodes();
776 }
778 void
779 sp_node_path_edit_delete_segment(void)
780 {
781 ShapeEditor *shape_editor = get_current_shape_editor();
782 if (shape_editor) shape_editor->delete_segment();
783 }
785 void
786 sp_node_path_edit_break(void)
787 {
788 ShapeEditor *shape_editor = get_current_shape_editor();
789 if (shape_editor) shape_editor->break_at_nodes();
790 }
792 void
793 sp_node_path_edit_join(void)
794 {
795 ShapeEditor *shape_editor = get_current_shape_editor();
796 if (shape_editor) shape_editor->join_nodes();
797 }
799 void
800 sp_node_path_edit_join_segment(void)
801 {
802 ShapeEditor *shape_editor = get_current_shape_editor();
803 if (shape_editor) shape_editor->join_segments();
804 }
806 void
807 sp_node_path_edit_toline(void)
808 {
809 ShapeEditor *shape_editor = get_current_shape_editor();
810 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
811 }
813 void
814 sp_node_path_edit_tocurve(void)
815 {
816 ShapeEditor *shape_editor = get_current_shape_editor();
817 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
818 }
820 void
821 sp_node_path_edit_cusp(void)
822 {
823 ShapeEditor *shape_editor = get_current_shape_editor();
824 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
825 }
827 void
828 sp_node_path_edit_smooth(void)
829 {
830 ShapeEditor *shape_editor = get_current_shape_editor();
831 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
832 }
834 void
835 sp_node_path_edit_symmetrical(void)
836 {
837 ShapeEditor *shape_editor = get_current_shape_editor();
838 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
839 }
841 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
842 bool show = gtk_toggle_action_get_active( act );
843 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
844 ShapeEditor *shape_editor = get_current_shape_editor();
845 if (shape_editor) shape_editor->show_handles(show);
846 }
848 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
849 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
850 }
852 /* is called when the node selection is modified */
853 static void
854 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
855 {
856 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
857 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
858 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
859 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
861 // quit if run by the attr_changed listener
862 if (g_object_get_data( tbl, "freeze" )) {
863 return;
864 }
866 // in turn, prevent listener from responding
867 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
869 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
870 SPUnit const *unit = tracker->getActiveUnit();
872 ShapeEditor *shape_editor = get_current_shape_editor();
873 if (shape_editor && shape_editor->has_nodepath()) {
874 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
875 int n_selected = 0;
876 if (nodepath) {
877 n_selected = nodepath->numSelected();
878 }
880 if (n_selected == 0) {
881 gtk_action_set_sensitive(xact, FALSE);
882 gtk_action_set_sensitive(yact, FALSE);
883 } else {
884 gtk_action_set_sensitive(xact, TRUE);
885 gtk_action_set_sensitive(yact, TRUE);
886 NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
887 NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
889 if (n_selected == 1) {
890 NR::Point sel_node = nodepath->singleSelectedCoords();
891 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
892 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
893 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
894 }
895 } else {
896 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
897 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
898 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
899 /* Note: Currently x and y will always have a value, even if the coordinates of the
900 selected nodes don't coincide (in this case we use the coordinates of the center
901 of the bounding box). So the entries are never set to zero. */
902 // FIXME: Maybe we should clear the entry if several nodes are selected
903 // instead of providing a kind of average value
904 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
905 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
906 }
907 }
908 }
909 } else {
910 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
911 gtk_action_set_sensitive(xact, FALSE);
912 gtk_action_set_sensitive(yact, FALSE);
913 }
915 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
916 }
918 static void
919 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
920 {
921 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
923 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
924 SPUnit const *unit = tracker->getActiveUnit();
926 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
927 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
928 }
930 // quit if run by the attr_changed listener
931 if (g_object_get_data( tbl, "freeze" )) {
932 return;
933 }
935 // in turn, prevent listener from responding
936 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
938 ShapeEditor *shape_editor = get_current_shape_editor();
939 if (shape_editor && shape_editor->has_nodepath()) {
940 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
941 if (!strcmp(value_name, "x")) {
942 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
943 }
944 if (!strcmp(value_name, "y")) {
945 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
946 }
947 }
949 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
950 }
952 static void
953 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
954 {
955 sp_node_path_value_changed(adj, tbl, "x");
956 }
958 static void
959 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
960 {
961 sp_node_path_value_changed(adj, tbl, "y");
962 }
964 //################################
965 //## Node Editing Toolbox ##
966 //################################
968 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
969 {
970 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
971 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
972 g_object_set_data( holder, "tracker", tracker );
974 {
975 InkAction* inky = ink_action_new( "NodeInsertAction",
976 _("Insert node"),
977 _("Insert new nodes into selected segments"),
978 "node_insert",
979 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
980 g_object_set( inky, "short_label", _("Insert"), NULL );
981 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
982 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
983 }
985 {
986 InkAction* inky = ink_action_new( "NodeDeleteAction",
987 _("Delete node"),
988 _("Delete selected nodes"),
989 "node_delete",
990 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
991 g_object_set( inky, "short_label", _("Delete"), NULL );
992 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
993 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
994 }
996 {
997 InkAction* inky = ink_action_new( "NodeJoinAction",
998 _("Join endnodes"),
999 _("Join selected endnodes"),
1000 "node_join",
1001 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1002 g_object_set( inky, "short_label", _("Join"), NULL );
1003 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1004 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1005 }
1007 {
1008 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1009 _("Join Segment"),
1010 _("Join selected endnodes with a new segment"),
1011 "node_join_segment",
1012 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1013 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1014 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1015 }
1017 {
1018 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1019 _("Delete Segment"),
1020 _("Split path between two non-endpoint nodes"),
1021 "node_delete_segment",
1022 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1023 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1024 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1025 }
1027 {
1028 InkAction* inky = ink_action_new( "NodeBreakAction",
1029 _("Node Break"),
1030 _("Break path at selected nodes"),
1031 "node_break",
1032 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1033 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1034 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1035 }
1037 {
1038 InkAction* inky = ink_action_new( "NodeCuspAction",
1039 _("Node Cusp"),
1040 _("Make selected nodes corner"),
1041 "node_cusp",
1042 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1043 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1044 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1045 }
1047 {
1048 InkAction* inky = ink_action_new( "NodeSmoothAction",
1049 _("Node Smooth"),
1050 _("Make selected nodes smooth"),
1051 "node_smooth",
1052 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1053 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1054 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1055 }
1057 {
1058 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1059 _("Node Symmetric"),
1060 _("Make selected nodes symmetric"),
1061 "node_symmetric",
1062 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1063 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1064 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1065 }
1067 {
1068 InkAction* inky = ink_action_new( "NodeLineAction",
1069 _("Node Line"),
1070 _("Make selected segments lines"),
1071 "node_line",
1072 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1073 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1074 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1075 }
1077 {
1078 InkAction* inky = ink_action_new( "NodeCurveAction",
1079 _("Node Curve"),
1080 _("Make selected segments curves"),
1081 "node_curve",
1082 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1083 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1084 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1085 }
1087 {
1088 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1089 _("Show Handles"),
1090 _("Show the Bezier handles of selected nodes"),
1091 "nodes_show_handles",
1092 Inkscape::ICON_SIZE_DECORATION );
1093 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1094 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1095 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1096 }
1098 {
1099 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1100 _("Next Path Effect Parameter"),
1101 _("Show next Path Effect parameter for editing"),
1102 "edit_next_parameter",
1103 Inkscape::ICON_SIZE_DECORATION );
1104 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1105 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1106 }
1108 /* X coord of selected node(s) */
1109 {
1110 EgeAdjustmentAction* eact = 0;
1111 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1112 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1113 eact = create_adjustment_action( "NodeXAction",
1114 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1115 "tools.nodes", "Xcoord", 0,
1116 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1117 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1118 labels, values, G_N_ELEMENTS(labels),
1119 sp_node_path_x_value_changed );
1120 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1121 g_object_set_data( holder, "nodes_x_action", eact );
1122 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1123 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1124 }
1126 /* Y coord of selected node(s) */
1127 {
1128 EgeAdjustmentAction* eact = 0;
1129 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1130 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1131 eact = create_adjustment_action( "NodeYAction",
1132 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1133 "tools.nodes", "Ycoord", 0,
1134 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1135 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1136 labels, values, G_N_ELEMENTS(labels),
1137 sp_node_path_y_value_changed );
1138 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1139 g_object_set_data( holder, "nodes_y_action", eact );
1140 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1141 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1142 }
1144 // add the units menu
1145 {
1146 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1147 gtk_action_group_add_action( mainActions, act );
1148 }
1150 sigc::connection *connection = new sigc::connection (
1151 desktop->connectToolSubselectionChanged(sigc::bind (sigc::ptr_fun(sp_node_toolbox_coord_changed), (GObject *)holder))
1152 );
1154 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1155 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1156 } // end of sp_node_toolbox_prep()
1159 //########################
1160 //## Zoom Toolbox ##
1161 //########################
1163 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1164 {
1165 // no custom GtkAction setup needed
1166 } // end of sp_zoom_toolbox_prep()
1168 void
1169 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1170 {
1171 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")));
1172 }
1175 void
1176 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1177 {
1178 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")));
1179 }
1181 void
1182 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1183 {
1184 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")));
1185 }
1187 static void
1188 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1189 {
1190 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1191 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1193 if (old_desktop) {
1194 GList *children, *iter;
1196 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1197 for ( iter = children ; iter ; iter = iter->next ) {
1198 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1199 }
1200 g_list_free(children);
1201 }
1203 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1205 if (desktop) {
1206 gtk_widget_set_sensitive(toolbox, TRUE);
1207 setup_func(toolbox, desktop);
1208 update_func(desktop, desktop->event_context, toolbox);
1209 *conn = desktop->connectEventContextChanged
1210 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1211 } else {
1212 gtk_widget_set_sensitive(toolbox, FALSE);
1213 }
1215 } // end of toolbox_set_desktop()
1218 static void
1219 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1220 {
1221 GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1222 gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1223 if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1224 // "toolbox.tools" was not set. Fallback to older value
1225 shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1227 // Copy the setting forwards
1228 prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1229 }
1230 Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1232 for (int i = 0 ; tools[i].type_name ; i++ ) {
1233 GtkWidget *button =
1234 sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1235 SP_BUTTON_TYPE_TOGGLE,
1236 Inkscape::Verb::get(tools[i].verb),
1237 Inkscape::Verb::get(tools[i].doubleclick_verb),
1238 desktop,
1239 tooltips );
1241 g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1242 (gpointer)button );
1243 }
1244 }
1247 static void
1248 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1249 {
1250 gchar const *const tname = ( eventcontext
1251 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1252 : NULL );
1253 for (int i = 0 ; tools[i].type_name ; i++ ) {
1254 SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1255 sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1256 }
1257 }
1259 static void
1260 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1261 {
1262 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1263 GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1264 GtkUIManager* mgr = gtk_ui_manager_new();
1265 GError* errVal = 0;
1266 gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1267 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1269 std::map<std::string, GtkWidget*> dataHolders;
1271 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1272 if ( aux_toolboxes[i].prep_func ) {
1273 // converted to GtkActions and UIManager
1275 GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1276 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1277 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1278 dataHolders[aux_toolboxes[i].type_name] = kludge;
1279 aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1280 } else {
1282 GtkWidget *sub_toolbox = 0;
1283 if (aux_toolboxes[i].create_func == NULL)
1284 sub_toolbox = sp_empty_toolbox_new(desktop);
1285 else {
1286 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1287 }
1289 gtk_size_group_add_widget( grouper, sub_toolbox );
1291 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1292 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1294 }
1295 }
1297 // Second pass to create toolbars *after* all GtkActions are created
1298 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1299 if ( aux_toolboxes[i].prep_func ) {
1300 // converted to GtkActions and UIManager
1302 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1304 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1305 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1307 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1308 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1309 g_free( tmp );
1310 tmp = 0;
1312 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1313 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1314 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1315 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1316 }
1317 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1320 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1322 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1323 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1324 swatch->setDesktop( desktop );
1325 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1326 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1327 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1328 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 );
1329 }
1331 gtk_widget_show_all( holder );
1332 sp_set_font_size_smaller( holder );
1334 gtk_size_group_add_widget( grouper, holder );
1336 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1337 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1338 }
1339 }
1341 g_object_unref( G_OBJECT(grouper) );
1342 }
1344 static void
1345 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1346 {
1347 gchar const *tname = ( eventcontext
1348 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1349 : NULL );
1350 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1351 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1352 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1353 gtk_widget_show_all(sub_toolbox);
1354 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1355 } else {
1356 gtk_widget_hide(sub_toolbox);
1357 }
1358 }
1359 }
1361 static void
1362 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1363 {
1364 gchar const * descr =
1365 "<ui>"
1366 " <toolbar name='CommandsToolbar'>"
1367 " <toolitem action='FileNew' />"
1368 " <toolitem action='FileOpen' />"
1369 " <toolitem action='FileSave' />"
1370 " <toolitem action='FilePrint' />"
1371 " <separator />"
1372 " <toolitem action='FileImport' />"
1373 " <toolitem action='FileExport' />"
1374 " <separator />"
1375 " <toolitem action='EditUndo' />"
1376 " <toolitem action='EditRedo' />"
1377 " <separator />"
1378 " <toolitem action='EditCopy' />"
1379 " <toolitem action='EditCut' />"
1380 " <toolitem action='EditPaste' />"
1381 " <separator />"
1382 " <toolitem action='ZoomSelection' />"
1383 " <toolitem action='ZoomDrawing' />"
1384 " <toolitem action='ZoomPage' />"
1385 " <separator />"
1386 " <toolitem action='EditDuplicate' />"
1387 " <toolitem action='EditClone' />"
1388 " <toolitem action='EditUnlinkClone' />"
1389 " <separator />"
1390 " <toolitem action='SelectionGroup' />"
1391 " <toolitem action='SelectionUnGroup' />"
1392 " <separator />"
1393 " <toolitem action='DialogFillStroke' />"
1394 " <toolitem action='DialogText' />"
1395 " <toolitem action='DialogXMLEditor' />"
1396 " <toolitem action='DialogAlignDistribute' />"
1397 " <separator />"
1398 " <toolitem action='DialogPreferences' />"
1399 " <toolitem action='DialogDocumentProperties' />"
1400 " </toolbar>"
1401 "</ui>";
1402 GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1405 GtkUIManager* mgr = gtk_ui_manager_new();
1406 GError* errVal = 0;
1408 gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1409 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1411 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1412 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1413 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1414 }
1415 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1416 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1417 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1420 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1421 }
1423 static void
1424 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1425 {
1426 }
1428 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1429 {
1430 gtk_widget_show(toolbox_toplevel);
1431 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1433 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1434 if (!shown_toolbox) {
1435 return;
1436 }
1437 gtk_widget_show(toolbox);
1439 gtk_widget_show_all(shown_toolbox);
1440 }
1442 void
1443 aux_toolbox_space(GtkWidget *tb, gint space)
1444 {
1445 gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1446 }
1448 static GtkWidget *
1449 sp_empty_toolbox_new(SPDesktop *desktop)
1450 {
1451 GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1452 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1453 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1455 gtk_widget_show_all(tbl);
1456 sp_set_font_size_smaller (tbl);
1458 return tbl;
1459 }
1461 // helper UI functions
1463 GtkWidget *
1464 sp_tb_spinbutton(
1465 gchar *label, gchar const *tooltip,
1466 gchar const *path, gchar const *data, gdouble def,
1467 GtkWidget *us,
1468 GtkWidget *tbl,
1469 gboolean altx, gchar const *altx_mark,
1470 gdouble lower, gdouble upper, gdouble step, gdouble page,
1471 void (*callback)(GtkAdjustment *, GtkWidget *),
1472 gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1473 {
1474 GtkTooltips *tt = gtk_tooltips_new();
1476 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1478 GtkWidget *l = gtk_label_new(label);
1479 gtk_widget_show(l);
1480 gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1481 gtk_container_add(GTK_CONTAINER(hb), l);
1483 GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1484 lower, upper, step, page, page);
1485 gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1486 if (us)
1487 sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1489 GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1490 gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1491 if (altx)
1492 gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1493 gtk_widget_set_size_request(sb,
1494 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1495 AUX_SPINBUTTON_HEIGHT);
1496 gtk_widget_show(sb);
1497 gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1498 gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1499 gtk_container_add(GTK_CONTAINER(hb), sb);
1500 gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1502 return hb;
1503 }
1505 #define MODE_LABEL_WIDTH 70
1507 //########################
1508 //## Star ##
1509 //########################
1511 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1512 {
1513 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1515 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1516 // do not remember prefs if this call is initiated by an undo change, because undoing object
1517 // creation sets bogus values to its attributes before it is deleted
1518 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1519 }
1521 // quit if run by the attr_changed listener
1522 if (g_object_get_data( dataKludge, "freeze" )) {
1523 return;
1524 }
1526 // in turn, prevent listener from responding
1527 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1529 bool modmade = false;
1531 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1532 GSList const *items = selection->itemList();
1533 for (; items != NULL; items = items->next) {
1534 if (SP_IS_STAR((SPItem *) items->data)) {
1535 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1536 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1537 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1538 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1539 + M_PI / (gint)adj->value));
1540 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1541 modmade = true;
1542 }
1543 }
1544 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1545 _("Star: Change number of corners"));
1547 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1548 }
1550 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1551 {
1552 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1554 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1555 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1556 }
1558 // quit if run by the attr_changed listener
1559 if (g_object_get_data( dataKludge, "freeze" )) {
1560 return;
1561 }
1563 // in turn, prevent listener from responding
1564 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1566 bool modmade = false;
1567 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1568 GSList const *items = selection->itemList();
1569 for (; items != NULL; items = items->next) {
1570 if (SP_IS_STAR((SPItem *) items->data)) {
1571 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1573 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1574 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1575 if (r2 < r1) {
1576 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1577 } else {
1578 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1579 }
1581 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1582 modmade = true;
1583 }
1584 }
1586 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1587 _("Star: Change spoke ratio"));
1589 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1590 }
1592 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1593 {
1594 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1595 bool flat = ege_select_one_action_get_active( act ) == 0;
1597 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1598 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1599 flat ? "true" : "false" );
1600 }
1602 // quit if run by the attr_changed listener
1603 if (g_object_get_data( dataKludge, "freeze" )) {
1604 return;
1605 }
1607 // in turn, prevent listener from responding
1608 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1610 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1611 GSList const *items = selection->itemList();
1612 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1613 bool modmade = false;
1615 if ( prop_action ) {
1616 gtk_action_set_sensitive( prop_action, !flat );
1617 }
1619 for (; items != NULL; items = items->next) {
1620 if (SP_IS_STAR((SPItem *) items->data)) {
1621 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1622 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1623 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1624 modmade = true;
1625 }
1626 }
1628 if (modmade) {
1629 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1630 flat ? _("Make polygon") : _("Make star"));
1631 }
1633 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1634 }
1636 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1637 {
1638 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1640 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1641 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1642 }
1644 // quit if run by the attr_changed listener
1645 if (g_object_get_data( dataKludge, "freeze" )) {
1646 return;
1647 }
1649 // in turn, prevent listener from responding
1650 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1652 bool modmade = false;
1654 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1655 GSList const *items = selection->itemList();
1656 for (; items != NULL; items = items->next) {
1657 if (SP_IS_STAR((SPItem *) items->data)) {
1658 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1659 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1660 SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1661 modmade = true;
1662 }
1663 }
1664 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1665 _("Star: Change rounding"));
1667 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1668 }
1670 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1671 {
1672 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1674 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1675 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1676 }
1678 // quit if run by the attr_changed listener
1679 if (g_object_get_data( dataKludge, "freeze" )) {
1680 return;
1681 }
1683 // in turn, prevent listener from responding
1684 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1686 bool modmade = false;
1688 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1689 GSList const *items = selection->itemList();
1690 for (; items != NULL; items = items->next) {
1691 if (SP_IS_STAR((SPItem *) items->data)) {
1692 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1693 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1694 SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1695 modmade = true;
1696 }
1697 }
1698 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1699 _("Star: Change randomization"));
1701 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1702 }
1705 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1706 gchar const */*old_value*/, gchar const */*new_value*/,
1707 bool /*is_interactive*/, gpointer data)
1708 {
1709 GtkWidget *tbl = GTK_WIDGET(data);
1711 // quit if run by the _changed callbacks
1712 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1713 return;
1714 }
1716 // in turn, prevent callbacks from responding
1717 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1719 GtkAdjustment *adj = 0;
1721 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1722 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1724 if (!strcmp(name, "inkscape:randomized")) {
1725 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1726 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1727 } else if (!strcmp(name, "inkscape:rounded")) {
1728 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1729 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1730 } else if (!strcmp(name, "inkscape:flatsided")) {
1731 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1732 char const *flatsides = repr->attribute("inkscape:flatsided");
1733 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1734 if ( flatsides && !strcmp(flatsides,"false") ) {
1735 ege_select_one_action_set_active( flat_action, 1 );
1736 gtk_action_set_sensitive( prop_action, TRUE );
1737 } else {
1738 ege_select_one_action_set_active( flat_action, 0 );
1739 gtk_action_set_sensitive( prop_action, FALSE );
1740 }
1741 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1742 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1743 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1744 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1745 if (r2 < r1) {
1746 gtk_adjustment_set_value(adj, r2/r1);
1747 } else {
1748 gtk_adjustment_set_value(adj, r1/r2);
1749 }
1750 } else if (!strcmp(name, "sodipodi:sides")) {
1751 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1752 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1753 }
1755 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1756 }
1759 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1760 {
1761 NULL, /* child_added */
1762 NULL, /* child_removed */
1763 star_tb_event_attr_changed,
1764 NULL, /* content_changed */
1765 NULL /* order_changed */
1766 };
1769 /**
1770 * \param selection Should not be NULL.
1771 */
1772 static void
1773 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1774 {
1775 int n_selected = 0;
1776 Inkscape::XML::Node *repr = NULL;
1778 purge_repr_listener( tbl, tbl );
1780 for (GSList const *items = selection->itemList();
1781 items != NULL;
1782 items = items->next)
1783 {
1784 if (SP_IS_STAR((SPItem *) items->data)) {
1785 n_selected++;
1786 repr = SP_OBJECT_REPR((SPItem *) items->data);
1787 }
1788 }
1790 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1792 if (n_selected == 0) {
1793 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1794 } else if (n_selected == 1) {
1795 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1797 if (repr) {
1798 g_object_set_data( tbl, "repr", repr );
1799 Inkscape::GC::anchor(repr);
1800 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1801 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1802 }
1803 } else {
1804 // FIXME: implement averaging of all parameters for multiple selected stars
1805 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1806 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1807 }
1808 }
1811 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1812 {
1813 // FIXME: in this and all other _default functions, set some flag telling the value_changed
1814 // callbacks to lump all the changes for all selected objects in one undo step
1816 GtkAdjustment *adj = 0;
1818 // fixme: make settable in prefs!
1819 gint mag = 5;
1820 gdouble prop = 0.5;
1821 gboolean flat = FALSE;
1822 gdouble randomized = 0;
1823 gdouble rounded = 0;
1825 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1826 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1828 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1829 gtk_action_set_sensitive( sb2, !flat );
1831 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1832 gtk_adjustment_set_value(adj, mag);
1833 gtk_adjustment_value_changed(adj);
1835 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1836 gtk_adjustment_set_value(adj, prop);
1837 gtk_adjustment_value_changed(adj);
1839 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1840 gtk_adjustment_set_value(adj, rounded);
1841 gtk_adjustment_value_changed(adj);
1843 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1844 gtk_adjustment_set_value(adj, randomized);
1845 gtk_adjustment_value_changed(adj);
1846 }
1849 void
1850 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1851 {
1852 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1853 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1854 GtkWidget *l = gtk_label_new(NULL);
1855 gtk_label_set_markup(GTK_LABEL(l), title);
1856 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1857 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1858 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1859 }
1862 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1863 {
1864 {
1865 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1866 ege_output_action_set_use_markup( act, TRUE );
1867 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1868 g_object_set_data( holder, "mode_action", act );
1869 }
1871 {
1872 EgeAdjustmentAction* eact = 0;
1873 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1874 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1876 /* Flatsided checkbox */
1877 {
1878 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1880 GtkTreeIter iter;
1881 gtk_list_store_append( model, &iter );
1882 gtk_list_store_set( model, &iter,
1883 0, _("Polygon"),
1884 1, _("Regular polygon (with one handle) instead of a star"),
1885 2, "star_flat",
1886 -1 );
1888 gtk_list_store_append( model, &iter );
1889 gtk_list_store_set( model, &iter,
1890 0, _("Star"),
1891 1, _("Star instead of a regular polygon (with one handle)"),
1892 2, "star_angled",
1893 -1 );
1895 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
1896 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1897 g_object_set_data( holder, "flat_action", act );
1899 ege_select_one_action_set_appearance( act, "full" );
1900 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1901 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1902 ege_select_one_action_set_icon_column( act, 2 );
1903 ege_select_one_action_set_tooltip_column( act, 1 );
1905 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1906 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1907 }
1909 /* Magnitude */
1910 {
1911 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1912 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1913 eact = create_adjustment_action( "MagnitudeAction",
1914 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
1915 "tools.shapes.star", "magnitude", 3,
1916 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1917 3, 1024, 1, 5,
1918 labels, values, G_N_ELEMENTS(labels),
1919 sp_stb_magnitude_value_changed,
1920 1.0, 0 );
1921 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1922 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1923 }
1925 /* Spoke ratio */
1926 {
1927 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1928 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1929 eact = create_adjustment_action( "SpokeAction",
1930 _("Spoke ratio"), _("Spoke ratio:"),
1931 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1932 // Base radius is the same for the closest handle.
1933 _("Base radius to tip radius ratio"),
1934 "tools.shapes.star", "proportion", 0.5,
1935 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1936 0.01, 1.0, 0.01, 0.1,
1937 labels, values, G_N_ELEMENTS(labels),
1938 sp_stb_proportion_value_changed );
1939 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1940 g_object_set_data( holder, "prop_action", eact );
1941 }
1943 if ( !isFlatSided ) {
1944 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1945 } else {
1946 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1947 }
1949 /* Roundedness */
1950 {
1951 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1952 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1953 eact = create_adjustment_action( "RoundednessAction",
1954 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1955 "tools.shapes.star", "rounded", 0.0,
1956 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1957 -10.0, 10.0, 0.01, 0.1,
1958 labels, values, G_N_ELEMENTS(labels),
1959 sp_stb_rounded_value_changed );
1960 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1961 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1962 }
1964 /* Randomization */
1965 {
1966 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1967 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1968 eact = create_adjustment_action( "RandomizationAction",
1969 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
1970 "tools.shapes.star", "randomized", 0.0,
1971 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1972 -10.0, 10.0, 0.001, 0.01,
1973 labels, values, G_N_ELEMENTS(labels),
1974 sp_stb_randomized_value_changed, 0.1, 3 );
1975 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1976 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1977 }
1978 }
1980 {
1981 /* Reset */
1982 {
1983 GtkAction* act = gtk_action_new( "StarResetAction",
1984 _("Defaults"),
1985 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1986 GTK_STOCK_CLEAR );
1987 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1988 gtk_action_group_add_action( mainActions, act );
1989 gtk_action_set_sensitive( act, TRUE );
1990 }
1991 }
1993 sigc::connection *connection = new sigc::connection(
1994 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
1995 );
1996 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1997 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1998 }
2001 //########################
2002 //## Rect ##
2003 //########################
2005 static void sp_rtb_sensitivize( GObject *tbl )
2006 {
2007 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2008 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2009 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2011 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2012 gtk_action_set_sensitive( not_rounded, FALSE );
2013 } else {
2014 gtk_action_set_sensitive( not_rounded, TRUE );
2015 }
2016 }
2019 static void
2020 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2021 void (*setter)(SPRect *, gdouble))
2022 {
2023 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2025 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2026 SPUnit const *unit = tracker->getActiveUnit();
2028 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2029 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2030 }
2032 // quit if run by the attr_changed listener
2033 if (g_object_get_data( tbl, "freeze" )) {
2034 return;
2035 }
2037 // in turn, prevent listener from responding
2038 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2040 bool modmade = false;
2041 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2042 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2043 if (SP_IS_RECT(items->data)) {
2044 if (adj->value != 0) {
2045 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2046 } else {
2047 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2048 }
2049 modmade = true;
2050 }
2051 }
2053 sp_rtb_sensitivize( tbl );
2055 if (modmade) {
2056 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2057 _("Change rectangle"));
2058 }
2060 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2061 }
2063 static void
2064 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2065 {
2066 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2067 }
2069 static void
2070 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2071 {
2072 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2073 }
2075 static void
2076 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2077 {
2078 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2079 }
2081 static void
2082 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2083 {
2084 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2085 }
2089 static void
2090 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2091 {
2092 GtkAdjustment *adj = 0;
2094 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2095 gtk_adjustment_set_value(adj, 0.0);
2096 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2097 gtk_adjustment_value_changed(adj);
2099 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2100 gtk_adjustment_set_value(adj, 0.0);
2101 gtk_adjustment_value_changed(adj);
2103 sp_rtb_sensitivize( obj );
2104 }
2106 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2107 gchar const */*old_value*/, gchar const */*new_value*/,
2108 bool /*is_interactive*/, gpointer data)
2109 {
2110 GObject *tbl = G_OBJECT(data);
2112 // quit if run by the _changed callbacks
2113 if (g_object_get_data( tbl, "freeze" )) {
2114 return;
2115 }
2117 // in turn, prevent callbacks from responding
2118 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2120 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2121 SPUnit const *unit = tracker->getActiveUnit();
2123 gpointer item = g_object_get_data( tbl, "item" );
2124 if (item && SP_IS_RECT(item)) {
2125 {
2126 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2127 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2128 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2129 }
2131 {
2132 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2133 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2134 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2135 }
2137 {
2138 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2139 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2140 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2141 }
2143 {
2144 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2145 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2146 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2147 }
2148 }
2150 sp_rtb_sensitivize( tbl );
2152 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2153 }
2156 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2157 NULL, /* child_added */
2158 NULL, /* child_removed */
2159 rect_tb_event_attr_changed,
2160 NULL, /* content_changed */
2161 NULL /* order_changed */
2162 };
2164 /**
2165 * \param selection should not be NULL.
2166 */
2167 static void
2168 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2169 {
2170 int n_selected = 0;
2171 Inkscape::XML::Node *repr = NULL;
2172 SPItem *item = NULL;
2174 if ( g_object_get_data( tbl, "repr" ) ) {
2175 g_object_set_data( tbl, "item", NULL );
2176 }
2177 purge_repr_listener( tbl, tbl );
2179 for (GSList const *items = selection->itemList();
2180 items != NULL;
2181 items = items->next) {
2182 if (SP_IS_RECT((SPItem *) items->data)) {
2183 n_selected++;
2184 item = (SPItem *) items->data;
2185 repr = SP_OBJECT_REPR(item);
2186 }
2187 }
2189 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2191 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2193 if (n_selected == 0) {
2194 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2196 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2197 gtk_action_set_sensitive(w, FALSE);
2198 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2199 gtk_action_set_sensitive(h, FALSE);
2201 } else if (n_selected == 1) {
2202 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2203 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2205 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2206 gtk_action_set_sensitive(w, TRUE);
2207 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2208 gtk_action_set_sensitive(h, TRUE);
2210 if (repr) {
2211 g_object_set_data( tbl, "repr", repr );
2212 g_object_set_data( tbl, "item", item );
2213 Inkscape::GC::anchor(repr);
2214 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2215 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2216 }
2217 } else {
2218 // FIXME: implement averaging of all parameters for multiple selected
2219 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2220 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2221 sp_rtb_sensitivize( tbl );
2222 }
2223 }
2226 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2227 {
2228 EgeAdjustmentAction* eact = 0;
2230 {
2231 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2232 ege_output_action_set_use_markup( act, TRUE );
2233 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2234 g_object_set_data( holder, "mode_action", act );
2235 }
2237 // rx/ry units menu: create
2238 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2239 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2240 // fixme: add % meaning per cent of the width/height
2241 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2242 g_object_set_data( holder, "tracker", tracker );
2244 /* W */
2245 {
2246 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2247 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2248 eact = create_adjustment_action( "RectWidthAction",
2249 _("Width"), _("W:"), _("Width of rectangle"),
2250 "tools.shapes.rect", "width", 0,
2251 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2252 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2253 labels, values, G_N_ELEMENTS(labels),
2254 sp_rtb_width_value_changed );
2255 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2256 g_object_set_data( holder, "width_action", eact );
2257 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2258 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2259 }
2261 /* H */
2262 {
2263 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2264 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2265 eact = create_adjustment_action( "RectHeightAction",
2266 _("Height"), _("H:"), _("Height of rectangle"),
2267 "tools.shapes.rect", "height", 0,
2268 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2269 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2270 labels, values, G_N_ELEMENTS(labels),
2271 sp_rtb_height_value_changed );
2272 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2273 g_object_set_data( holder, "height_action", eact );
2274 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2275 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2276 }
2278 /* rx */
2279 {
2280 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2281 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2282 eact = create_adjustment_action( "RadiusXAction",
2283 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2284 "tools.shapes.rect", "rx", 0,
2285 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2286 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2287 labels, values, G_N_ELEMENTS(labels),
2288 sp_rtb_rx_value_changed);
2289 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2290 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2291 }
2293 /* ry */
2294 {
2295 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2296 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2297 eact = create_adjustment_action( "RadiusYAction",
2298 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2299 "tools.shapes.rect", "ry", 0,
2300 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2301 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2302 labels, values, G_N_ELEMENTS(labels),
2303 sp_rtb_ry_value_changed);
2304 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2305 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2306 }
2308 // add the units menu
2309 {
2310 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2311 gtk_action_group_add_action( mainActions, act );
2312 }
2314 /* Reset */
2315 {
2316 InkAction* inky = ink_action_new( "RectResetAction",
2317 _("Not rounded"),
2318 _("Make corners sharp"),
2319 "squared_corner",
2320 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2321 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2322 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2323 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2324 g_object_set_data( holder, "not_rounded", inky );
2325 }
2327 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2328 sp_rtb_sensitivize( holder );
2330 sigc::connection *connection = new sigc::connection(
2331 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2332 );
2333 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2334 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2335 }
2337 //########################
2338 //## 3D Box ##
2339 //########################
2341 // normalize angle so that it lies in the interval [0,360]
2342 static double box3d_normalize_angle (double a) {
2343 double angle = a + ((int) (a/360.0))*360;
2344 if (angle < 0) {
2345 angle += 360.0;
2346 }
2347 return angle;
2348 }
2350 static void
2351 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2352 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2353 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2354 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2355 // are reset).
2356 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2358 if (is_infinite) {
2359 gtk_toggle_action_set_active(tact, TRUE);
2360 gtk_action_set_sensitive(act, TRUE);
2362 double angle = persp3d_get_infinite_angle(persp, axis);
2363 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2364 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2365 }
2366 } else {
2367 gtk_toggle_action_set_active(tact, FALSE);
2368 gtk_action_set_sensitive(act, FALSE);
2369 }
2370 }
2372 static void
2373 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2374 if (!persp_repr) {
2375 g_print ("No perspective given to box3d_resync_toolbar().\n");
2376 return;
2377 }
2379 GtkWidget *tbl = GTK_WIDGET(data);
2380 GtkAdjustment *adj = 0;
2381 GtkAction *act = 0;
2382 GtkToggleAction *tact = 0;
2383 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2384 {
2385 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2386 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2387 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2389 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2390 }
2391 {
2392 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2393 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2394 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2396 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2397 }
2398 {
2399 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2400 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2401 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2403 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2404 }
2405 }
2407 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2408 gchar const */*old_value*/, gchar const */*new_value*/,
2409 bool /*is_interactive*/, gpointer data)
2410 {
2411 GtkWidget *tbl = GTK_WIDGET(data);
2413 // quit if run by the attr_changed listener
2414 // note: it used to work without the differently called freeze_ attributes (here and in
2415 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2416 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2417 return;
2418 }
2420 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2421 // sp_document_maybe_done() when the document is undo insensitive)
2422 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2424 // TODO: Only update the appropriate part of the toolbar
2425 // if (!strcmp(name, "inkscape:vp_z")) {
2426 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2427 // }
2429 Persp3D *persp = persp3d_get_from_repr(repr);
2430 persp3d_update_box_reprs(persp);
2432 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2433 }
2435 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2436 {
2437 NULL, /* child_added */
2438 NULL, /* child_removed */
2439 box3d_persp_tb_event_attr_changed,
2440 NULL, /* content_changed */
2441 NULL /* order_changed */
2442 };
2444 /**
2445 * \param selection Should not be NULL.
2446 */
2447 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2448 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2449 static void
2450 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2451 {
2452 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2453 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2454 // update the perspectives with infinite VPs and leave the other ones untouched).
2456 Inkscape::XML::Node *persp_repr = NULL;
2457 purge_repr_listener(tbl, tbl);
2459 SPItem *item = selection->singleItem();
2460 if (item && SP_IS_BOX3D(item)) {
2461 // FIXME: Also deal with multiple selected boxes
2462 SPBox3D *box = SP_BOX3D(item);
2463 Persp3D *persp = box3d_get_perspective(box);
2464 persp_repr = SP_OBJECT_REPR(persp);
2465 if (persp_repr) {
2466 g_object_set_data(tbl, "repr", persp_repr);
2467 Inkscape::GC::anchor(persp_repr);
2468 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2469 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2470 }
2472 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2473 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2475 box3d_resync_toolbar(persp_repr, tbl);
2476 }
2477 }
2479 static void
2480 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2481 {
2482 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2483 SPDocument *document = sp_desktop_document(desktop);
2485 // quit if run by the attr_changed listener
2486 // note: it used to work without the differently called freeze_ attributes (here and in
2487 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2488 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2489 return;
2490 }
2492 // in turn, prevent listener from responding
2493 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2495 //Persp3D *persp = document->current_persp3d;
2496 std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
2497 if (sel_persps.empty()) {
2498 // this can happen when the document is created; we silently ignore it
2499 return;
2500 }
2501 Persp3D *persp = *(sel_persps.begin());
2503 persp->tmat.set_infinite_direction (axis, adj->value);
2504 SP_OBJECT(persp)->updateRepr();
2506 // TODO: use the correct axis here, too
2507 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2509 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2510 }
2513 static void
2514 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2515 {
2516 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2517 }
2519 static void
2520 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2521 {
2522 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2523 }
2525 static void
2526 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2527 {
2528 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2529 }
2532 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction *box3d_angle, Proj::Axis axis )
2533 {
2534 // TODO: Take all selected perspectives into account
2535 std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
2536 if (sel_persps.empty()) {
2537 // this can happen when the document is created; we silently ignore it
2538 return;
2539 }
2540 Persp3D *persp = *(sel_persps.begin());
2542 bool set_infinite = gtk_toggle_action_get_active(act);
2543 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2544 }
2546 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2547 {
2548 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2549 }
2551 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2552 {
2553 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2554 }
2556 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2557 {
2558 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2559 }
2561 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2562 {
2563 EgeAdjustmentAction* eact = 0;
2564 SPDocument *document = sp_desktop_document (desktop);
2565 Persp3D *persp = document->current_persp3d;
2567 EgeAdjustmentAction* box3d_angle_x = 0;
2568 EgeAdjustmentAction* box3d_angle_y = 0;
2569 EgeAdjustmentAction* box3d_angle_z = 0;
2571 /* Angle X */
2572 {
2573 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2574 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2575 eact = create_adjustment_action( "3DBoxAngleXAction",
2576 _("Angle in X direction"), _("Angle X:"),
2577 // Translators: PL is short for 'perspective line'
2578 _("Angle of PLs in X direction"),
2579 "tools.shapes.3dbox", "box3d_angle_x", 30,
2580 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2581 -360.0, 360.0, 1.0, 10.0,
2582 labels, values, G_N_ELEMENTS(labels),
2583 box3d_angle_x_value_changed );
2584 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2585 g_object_set_data( holder, "box3d_angle_x_action", eact );
2586 box3d_angle_x = eact;
2587 }
2589 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2590 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2591 } else {
2592 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2593 }
2596 /* VP X state */
2597 {
2598 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2599 // Translators: VP is short for 'vanishing point'
2600 _("State of VP in X direction"),
2601 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2602 "toggle_vp_x",
2603 Inkscape::ICON_SIZE_DECORATION );
2604 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2605 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2606 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2607 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2608 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2609 }
2611 /* Angle Y */
2612 {
2613 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2614 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2615 eact = create_adjustment_action( "3DBoxAngleYAction",
2616 _("Angle in Y direction"), _("Angle Y:"),
2617 // Translators: PL is short for 'perspective line'
2618 _("Angle of PLs in Y direction"),
2619 "tools.shapes.3dbox", "box3d_angle_y", 30,
2620 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2621 -360.0, 360.0, 1.0, 10.0,
2622 labels, values, G_N_ELEMENTS(labels),
2623 box3d_angle_y_value_changed );
2624 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2625 g_object_set_data( holder, "box3d_angle_y_action", eact );
2626 box3d_angle_y = eact;
2627 }
2629 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2630 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2631 } else {
2632 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2633 }
2635 /* VP Y state */
2636 {
2637 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2638 // Translators: VP is short for 'vanishing point'
2639 _("State of VP in Y direction"),
2640 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2641 "toggle_vp_y",
2642 Inkscape::ICON_SIZE_DECORATION );
2643 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2644 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2645 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2646 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2647 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2648 }
2650 /* Angle Z */
2651 {
2652 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2653 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2654 eact = create_adjustment_action( "3DBoxAngleZAction",
2655 _("Angle in Z direction"), _("Angle Z:"),
2656 // Translators: PL is short for 'perspective line'
2657 _("Angle of PLs in Z direction"),
2658 "tools.shapes.3dbox", "box3d_angle_z", 30,
2659 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2660 -360.0, 360.0, 1.0, 10.0,
2661 labels, values, G_N_ELEMENTS(labels),
2662 box3d_angle_z_value_changed );
2663 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2664 g_object_set_data( holder, "box3d_angle_z_action", eact );
2665 box3d_angle_z = eact;
2666 }
2668 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2669 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2670 } else {
2671 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2672 }
2674 /* VP Z state */
2675 {
2676 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2677 // Translators: VP is short for 'vanishing point'
2678 _("State of VP in Z direction"),
2679 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2680 "toggle_vp_z",
2681 Inkscape::ICON_SIZE_DECORATION );
2682 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2683 g_object_set_data( holder, "box3d_vp_z_state_action", act );
2684 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2685 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2686 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2687 }
2689 sigc::connection *connection = new sigc::connection(
2690 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2691 );
2692 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2693 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2694 }
2696 //########################
2697 //## Spiral ##
2698 //########################
2700 static void
2701 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2702 {
2703 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2705 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2706 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2707 }
2709 // quit if run by the attr_changed listener
2710 if (g_object_get_data( tbl, "freeze" )) {
2711 return;
2712 }
2714 // in turn, prevent listener from responding
2715 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2717 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2719 bool modmade = false;
2720 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2721 items != NULL;
2722 items = items->next)
2723 {
2724 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2725 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2726 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2727 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2728 modmade = true;
2729 }
2730 }
2732 g_free(namespaced_name);
2734 if (modmade) {
2735 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2736 _("Change spiral"));
2737 }
2739 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2740 }
2742 static void
2743 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2744 {
2745 sp_spl_tb_value_changed(adj, tbl, "revolution");
2746 }
2748 static void
2749 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2750 {
2751 sp_spl_tb_value_changed(adj, tbl, "expansion");
2752 }
2754 static void
2755 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2756 {
2757 sp_spl_tb_value_changed(adj, tbl, "t0");
2758 }
2760 static void
2761 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2762 {
2763 GtkWidget *tbl = GTK_WIDGET(obj);
2765 GtkAdjustment *adj;
2767 // fixme: make settable
2768 gdouble rev = 5;
2769 gdouble exp = 1.0;
2770 gdouble t0 = 0.0;
2772 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2773 gtk_adjustment_set_value(adj, rev);
2774 gtk_adjustment_value_changed(adj);
2776 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2777 gtk_adjustment_set_value(adj, exp);
2778 gtk_adjustment_value_changed(adj);
2780 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2781 gtk_adjustment_set_value(adj, t0);
2782 gtk_adjustment_value_changed(adj);
2784 spinbutton_defocus(GTK_OBJECT(tbl));
2785 }
2788 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2789 gchar const */*old_value*/, gchar const */*new_value*/,
2790 bool /*is_interactive*/, gpointer data)
2791 {
2792 GtkWidget *tbl = GTK_WIDGET(data);
2794 // quit if run by the _changed callbacks
2795 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2796 return;
2797 }
2799 // in turn, prevent callbacks from responding
2800 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2802 GtkAdjustment *adj;
2803 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2804 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2806 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2807 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2809 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2810 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2812 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2813 }
2816 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2817 NULL, /* child_added */
2818 NULL, /* child_removed */
2819 spiral_tb_event_attr_changed,
2820 NULL, /* content_changed */
2821 NULL /* order_changed */
2822 };
2824 static void
2825 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2826 {
2827 int n_selected = 0;
2828 Inkscape::XML::Node *repr = NULL;
2830 purge_repr_listener( tbl, tbl );
2832 for (GSList const *items = selection->itemList();
2833 items != NULL;
2834 items = items->next)
2835 {
2836 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2837 n_selected++;
2838 repr = SP_OBJECT_REPR((SPItem *) items->data);
2839 }
2840 }
2842 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2844 if (n_selected == 0) {
2845 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2846 } else if (n_selected == 1) {
2847 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2849 if (repr) {
2850 g_object_set_data( tbl, "repr", repr );
2851 Inkscape::GC::anchor(repr);
2852 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2853 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2854 }
2855 } else {
2856 // FIXME: implement averaging of all parameters for multiple selected
2857 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2858 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2859 }
2860 }
2863 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2864 {
2865 EgeAdjustmentAction* eact = 0;
2867 {
2868 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2869 ege_output_action_set_use_markup( act, TRUE );
2870 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2871 g_object_set_data( holder, "mode_action", act );
2872 }
2874 /* Revolution */
2875 {
2876 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2877 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2878 eact = create_adjustment_action( "SpiralRevolutionAction",
2879 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
2880 "tools.shapes.spiral", "revolution", 3.0,
2881 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2882 0.01, 1024.0, 0.1, 1.0,
2883 labels, values, G_N_ELEMENTS(labels),
2884 sp_spl_tb_revolution_value_changed, 1, 2);
2885 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2886 }
2888 /* Expansion */
2889 {
2890 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2891 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2892 eact = create_adjustment_action( "SpiralExpansionAction",
2893 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2894 "tools.shapes.spiral", "expansion", 1.0,
2895 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2896 0.0, 1000.0, 0.01, 1.0,
2897 labels, values, G_N_ELEMENTS(labels),
2898 sp_spl_tb_expansion_value_changed);
2899 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2900 }
2902 /* T0 */
2903 {
2904 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2905 gdouble values[] = {0, 0.5, 0.9};
2906 eact = create_adjustment_action( "SpiralT0Action",
2907 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2908 "tools.shapes.spiral", "t0", 0.0,
2909 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2910 0.0, 0.999, 0.01, 1.0,
2911 labels, values, G_N_ELEMENTS(labels),
2912 sp_spl_tb_t0_value_changed);
2913 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2914 }
2916 /* Reset */
2917 {
2918 InkAction* inky = ink_action_new( "SpiralResetAction",
2919 _("Defaults"),
2920 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2921 GTK_STOCK_CLEAR,
2922 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2923 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2924 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2925 }
2928 sigc::connection *connection = new sigc::connection(
2929 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2930 );
2931 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2932 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2933 }
2935 //########################
2936 //## Pen/Pencil ##
2937 //########################
2940 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2941 {
2942 // Put stuff here
2943 }
2945 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2946 {
2947 // Put stuff here
2948 }
2950 //########################
2951 //## Tweak ##
2952 //########################
2954 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2955 {
2956 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2957 }
2959 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2960 {
2961 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2962 }
2964 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
2965 {
2966 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2967 }
2969 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2970 {
2971 int mode = ege_select_one_action_get_active( act );
2972 prefs_set_int_attribute("tools.tweak", "mode", mode);
2974 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2975 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2976 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2977 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2978 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2979 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2980 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
2981 if (doh) gtk_action_set_sensitive (doh, TRUE);
2982 if (dos) gtk_action_set_sensitive (dos, TRUE);
2983 if (dol) gtk_action_set_sensitive (dol, TRUE);
2984 if (doo) gtk_action_set_sensitive (doo, TRUE);
2985 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
2986 if (fid) gtk_action_set_sensitive (fid, FALSE);
2987 } else {
2988 if (doh) gtk_action_set_sensitive (doh, FALSE);
2989 if (dos) gtk_action_set_sensitive (dos, FALSE);
2990 if (dol) gtk_action_set_sensitive (dol, FALSE);
2991 if (doo) gtk_action_set_sensitive (doo, FALSE);
2992 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
2993 if (fid) gtk_action_set_sensitive (fid, TRUE);
2994 }
2995 }
2997 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2998 {
2999 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3000 }
3002 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3003 bool show = gtk_toggle_action_get_active( act );
3004 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3005 }
3006 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3007 bool show = gtk_toggle_action_get_active( act );
3008 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3009 }
3010 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3011 bool show = gtk_toggle_action_get_active( act );
3012 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3013 }
3014 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3015 bool show = gtk_toggle_action_get_active( act );
3016 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3017 }
3019 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3020 {
3021 {
3022 /* Width */
3023 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3024 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3025 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3026 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3027 "tools.tweak", "width", 15,
3028 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3029 1, 100, 1.0, 10.0,
3030 labels, values, G_N_ELEMENTS(labels),
3031 sp_tweak_width_value_changed, 0.01, 0, 100 );
3032 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3033 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3034 }
3037 {
3038 /* Force */
3039 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3040 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3041 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3042 _("Force"), _("Force:"), _("The force of the tweak action"),
3043 "tools.tweak", "force", 20,
3044 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3045 1, 100, 1.0, 10.0,
3046 labels, values, G_N_ELEMENTS(labels),
3047 sp_tweak_force_value_changed, 0.01, 0, 100 );
3048 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3049 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3050 }
3052 /* Mode */
3053 {
3054 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3056 GtkTreeIter iter;
3057 gtk_list_store_append( model, &iter );
3058 gtk_list_store_set( model, &iter,
3059 0, _("Push mode"),
3060 1, _("Push parts of paths in any direction"),
3061 2, "tweak_push_mode",
3062 -1 );
3064 gtk_list_store_append( model, &iter );
3065 gtk_list_store_set( model, &iter,
3066 0, _("Shrink mode"),
3067 1, _("Shrink (inset) parts of paths"),
3068 2, "tweak_shrink_mode",
3069 -1 );
3071 gtk_list_store_append( model, &iter );
3072 gtk_list_store_set( model, &iter,
3073 0, _("Grow mode"),
3074 1, _("Grow (outset) parts of paths"),
3075 2, "tweak_grow_mode",
3076 -1 );
3078 gtk_list_store_append( model, &iter );
3079 gtk_list_store_set( model, &iter,
3080 0, _("Attract mode"),
3081 1, _("Attract parts of paths towards cursor"),
3082 2, "tweak_attract_mode",
3083 -1 );
3085 gtk_list_store_append( model, &iter );
3086 gtk_list_store_set( model, &iter,
3087 0, _("Repel mode"),
3088 1, _("Repel parts of paths from cursor"),
3089 2, "tweak_repel_mode",
3090 -1 );
3092 gtk_list_store_append( model, &iter );
3093 gtk_list_store_set( model, &iter,
3094 0, _("Roughen mode"),
3095 1, _("Roughen parts of paths"),
3096 2, "tweak_roughen_mode",
3097 -1 );
3099 gtk_list_store_append( model, &iter );
3100 gtk_list_store_set( model, &iter,
3101 0, _("Color paint mode"),
3102 1, _("Paint the tool's color upon selected objects"),
3103 2, "tweak_colorpaint_mode",
3104 -1 );
3106 gtk_list_store_append( model, &iter );
3107 gtk_list_store_set( model, &iter,
3108 0, _("Color jitter mode"),
3109 1, _("Jitter the colors of selected objects"),
3110 2, "tweak_colorjitter_mode",
3111 -1 );
3113 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3114 g_object_set( act, "short_label", _("Mode:"), NULL );
3115 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3116 g_object_set_data( holder, "mode_action", act );
3118 ege_select_one_action_set_appearance( act, "full" );
3119 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3120 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3121 ege_select_one_action_set_icon_column( act, 2 );
3122 ege_select_one_action_set_tooltip_column( act, 1 );
3124 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3125 ege_select_one_action_set_active( act, mode );
3126 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3128 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3129 }
3131 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3133 {
3134 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3135 ege_output_action_set_use_markup( act, TRUE );
3136 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3137 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3138 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3139 g_object_set_data( holder, "tweak_channels_label", act);
3140 }
3142 {
3143 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3144 _("Hue"),
3145 _("In color mode, act on objects' hue"),
3146 NULL,
3147 Inkscape::ICON_SIZE_DECORATION );
3148 g_object_set( act, "short_label", _("H"), NULL );
3149 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3150 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3151 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3152 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3153 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3154 g_object_set_data( holder, "tweak_doh", act);
3155 }
3156 {
3157 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3158 _("Saturation"),
3159 _("In color mode, act on objects' saturation"),
3160 NULL,
3161 Inkscape::ICON_SIZE_DECORATION );
3162 g_object_set( act, "short_label", _("S"), NULL );
3163 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3164 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3165 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3166 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3167 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3168 g_object_set_data( holder, "tweak_dos", act );
3169 }
3170 {
3171 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3172 _("Lightness"),
3173 _("In color mode, act on objects' lightness"),
3174 NULL,
3175 Inkscape::ICON_SIZE_DECORATION );
3176 g_object_set( act, "short_label", _("L"), NULL );
3177 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3178 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3179 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3180 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3181 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3182 g_object_set_data( holder, "tweak_dol", act );
3183 }
3184 {
3185 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3186 _("Opacity"),
3187 _("In color mode, act on objects' opacity"),
3188 NULL,
3189 Inkscape::ICON_SIZE_DECORATION );
3190 g_object_set( act, "short_label", _("O"), NULL );
3191 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3192 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3193 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3194 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3195 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3196 g_object_set_data( holder, "tweak_doo", act );
3197 }
3199 { /* Fidelity */
3200 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3201 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3202 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3203 _("Fidelity"), _("Fidelity:"),
3204 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3205 "tools.tweak", "fidelity", 50,
3206 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3207 1, 100, 1.0, 10.0,
3208 labels, values, G_N_ELEMENTS(labels),
3209 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3210 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3211 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3212 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3213 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3214 g_object_set_data( holder, "tweak_fidelity", eact );
3215 }
3218 /* Use Pressure button */
3219 {
3220 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3221 _("Pressure"),
3222 _("Use the pressure of the input device to alter the force of tweak action"),
3223 "use_pressure",
3224 Inkscape::ICON_SIZE_DECORATION );
3225 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3226 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3227 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3228 }
3230 }
3233 //########################
3234 //## Calligraphy ##
3235 //########################
3237 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3238 {
3239 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3240 }
3242 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3243 {
3244 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3245 }
3247 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3248 {
3249 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3250 }
3252 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3253 {
3254 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3255 }
3257 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3258 {
3259 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3260 }
3262 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3263 {
3264 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3265 }
3267 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3268 {
3269 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3270 }
3272 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3273 {
3274 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3275 }
3277 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3278 {
3279 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3280 }
3282 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3283 {
3284 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3285 }
3287 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3288 {
3289 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3291 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3292 }
3294 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3295 {
3296 // FIXME: make defaults settable via Inkscape Options
3297 struct KeyValue {
3298 char const *key;
3299 double value;
3300 } const key_values[] = {
3301 {"mass", 0.02},
3302 {"wiggle", 0.0},
3303 {"angle", 30.0},
3304 {"width", 15},
3305 {"thinning", 0.1},
3306 {"tremor", 0.0},
3307 {"flatness", 0.9},
3308 {"cap_rounding", 0.0}
3309 };
3311 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3312 KeyValue const &kv = key_values[i];
3313 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3314 if ( adj ) {
3315 gtk_adjustment_set_value(adj, kv.value);
3316 }
3317 }
3318 }
3321 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3322 {
3323 {
3324 EgeAdjustmentAction* calligraphy_angle = 0;
3326 {
3327 /* Width */
3328 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3329 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3330 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3331 _("Pen Width"), _("Width:"),
3332 _("The width of the calligraphic pen (relative to the visible canvas area)"),
3333 "tools.calligraphic", "width", 15,
3334 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3335 1, 100, 1.0, 10.0,
3336 labels, values, G_N_ELEMENTS(labels),
3337 sp_ddc_width_value_changed, 0.01, 0, 100 );
3338 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3339 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3340 }
3342 {
3343 /* Thinning */
3344 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3345 gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3346 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3347 _("Stroke Thinning"), _("Thinning:"),
3348 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3349 "tools.calligraphic", "thinning", 0.1,
3350 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3351 -1.0, 1.0, 0.01, 0.1,
3352 labels, values, G_N_ELEMENTS(labels),
3353 sp_ddc_velthin_value_changed, 0.01, 2);
3354 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3355 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3356 }
3358 {
3359 /* Angle */
3360 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3361 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3362 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3363 _("Pen Angle"), _("Angle:"),
3364 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3365 "tools.calligraphic", "angle", 30,
3366 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3367 -90.0, 90.0, 1.0, 10.0,
3368 labels, values, G_N_ELEMENTS(labels),
3369 sp_ddc_angle_value_changed, 1, 0 );
3370 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3371 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3372 calligraphy_angle = eact;
3373 }
3375 {
3376 /* Fixation */
3377 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3378 gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3379 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3380 _("Fixation"), _("Fixation:"),
3381 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3382 "tools.calligraphic", "flatness", 0.9,
3383 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3384 0.0, 1.0, 0.01, 0.1,
3385 labels, values, G_N_ELEMENTS(labels),
3386 sp_ddc_flatness_value_changed, 0.01, 2 );
3387 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3388 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3389 }
3391 {
3392 /* Cap Rounding */
3393 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3394 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3395 // TRANSLATORS: "cap" means "end" (both start and finish) here
3396 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3397 _("Cap rounding"), _("Caps:"),
3398 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3399 "tools.calligraphic", "cap_rounding", 0.0,
3400 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3401 0.0, 5.0, 0.01, 0.1,
3402 labels, values, G_N_ELEMENTS(labels),
3403 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3404 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3405 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3406 }
3408 {
3409 /* Tremor */
3410 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3411 gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3412 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3413 _("Stroke Tremor"), _("Tremor:"),
3414 _("Increase to make strokes rugged and trembling"),
3415 "tools.calligraphic", "tremor", 0.0,
3416 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3417 0.0, 1.0, 0.01, 0.1,
3418 labels, values, G_N_ELEMENTS(labels),
3419 sp_ddc_tremor_value_changed, 0.01, 2 );
3421 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3422 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3423 }
3425 {
3426 /* Wiggle */
3427 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3428 gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3429 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3430 _("Pen Wiggle"), _("Wiggle:"),
3431 _("Increase to make the pen waver and wiggle"),
3432 "tools.calligraphic", "wiggle", 0.0,
3433 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3434 0.0, 1.0, 0.01, 0.1,
3435 labels, values, G_N_ELEMENTS(labels),
3436 sp_ddc_wiggle_value_changed, 0.01, 2 );
3437 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3438 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3439 }
3441 {
3442 /* Mass */
3443 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3444 gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3445 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3446 _("Pen Mass"), _("Mass:"),
3447 _("Increase to make the pen drag behind, as if slowed by inertia"),
3448 "tools.calligraphic", "mass", 0.02,
3449 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3450 0.0, 1.0, 0.01, 0.1,
3451 labels, values, G_N_ELEMENTS(labels),
3452 sp_ddc_mass_value_changed, 0.01, 2 );
3453 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3454 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3455 }
3458 /* Trace Background button */
3459 {
3460 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3461 _("Trace Background"),
3462 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3463 "trace_background",
3464 Inkscape::ICON_SIZE_DECORATION );
3465 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3466 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3467 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3468 }
3470 /* Use Pressure button */
3471 {
3472 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3473 _("Pressure"),
3474 _("Use the pressure of the input device to alter the width of the pen"),
3475 "use_pressure",
3476 Inkscape::ICON_SIZE_DECORATION );
3477 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3478 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3479 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3480 }
3482 /* Use Tilt button */
3483 {
3484 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3485 _("Tilt"),
3486 _("Use the tilt of the input device to alter the angle of the pen's nib"),
3487 "use_tilt",
3488 Inkscape::ICON_SIZE_DECORATION );
3489 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3490 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3491 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3492 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3493 }
3495 /* Reset */
3496 {
3497 GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3498 _("Defaults"),
3499 _("Reset all parameters to defaults"),
3500 GTK_STOCK_CLEAR );
3501 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3502 gtk_action_group_add_action( mainActions, act );
3503 gtk_action_set_sensitive( act, TRUE );
3504 }
3505 }
3506 }
3509 //########################
3510 //## Circle / Arc ##
3511 //########################
3513 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3514 {
3515 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3516 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3518 if (v1 == 0 && v2 == 0) {
3519 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3520 gtk_action_set_sensitive( ocb, FALSE );
3521 gtk_action_set_sensitive( make_whole, FALSE );
3522 }
3523 } else {
3524 gtk_action_set_sensitive( ocb, TRUE );
3525 gtk_action_set_sensitive( make_whole, TRUE );
3526 }
3527 }
3529 static void
3530 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3531 {
3532 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3534 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3535 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3536 }
3538 // quit if run by the attr_changed listener
3539 if (g_object_get_data( tbl, "freeze" )) {
3540 return;
3541 }
3543 // in turn, prevent listener from responding
3544 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3546 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3548 bool modmade = false;
3549 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3550 items != NULL;
3551 items = items->next)
3552 {
3553 SPItem *item = SP_ITEM(items->data);
3555 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3557 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3558 SPArc *arc = SP_ARC(item);
3560 if (!strcmp(value_name, "start"))
3561 ge->start = (adj->value * M_PI)/ 180;
3562 else
3563 ge->end = (adj->value * M_PI)/ 180;
3565 sp_genericellipse_normalize(ge);
3566 ((SPObject *)arc)->updateRepr();
3567 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3569 modmade = true;
3570 }
3571 }
3573 g_free(namespaced_name);
3575 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3577 sp_arctb_sensitivize( tbl, adj->value, other->value );
3579 if (modmade) {
3580 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3581 _("Arc: Change start/end"));
3582 }
3584 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3585 }
3588 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
3589 {
3590 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
3591 }
3593 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3594 {
3595 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
3596 }
3598 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3599 {
3600 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3601 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3602 if ( ege_select_one_action_get_active( act ) != 0 ) {
3603 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3604 } else {
3605 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3606 }
3607 }
3609 // quit if run by the attr_changed listener
3610 if (g_object_get_data( tbl, "freeze" )) {
3611 return;
3612 }
3614 // in turn, prevent listener from responding
3615 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3617 bool modmade = false;
3619 if ( ege_select_one_action_get_active(act) != 0 ) {
3620 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3621 items != NULL;
3622 items = items->next)
3623 {
3624 if (SP_IS_ARC((SPItem *) items->data)) {
3625 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3626 repr->setAttribute("sodipodi:open", "true");
3627 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3628 modmade = true;
3629 }
3630 }
3631 } else {
3632 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3633 items != NULL;
3634 items = items->next)
3635 {
3636 if (SP_IS_ARC((SPItem *) items->data)) {
3637 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3638 repr->setAttribute("sodipodi:open", NULL);
3639 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3640 modmade = true;
3641 }
3642 }
3643 }
3645 if (modmade) {
3646 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3647 _("Arc: Change open/closed"));
3648 }
3650 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3651 }
3653 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3654 {
3655 GtkAdjustment *adj;
3656 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3657 gtk_adjustment_set_value(adj, 0.0);
3658 gtk_adjustment_value_changed(adj);
3660 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3661 gtk_adjustment_set_value(adj, 0.0);
3662 gtk_adjustment_value_changed(adj);
3664 spinbutton_defocus( GTK_OBJECT(obj) );
3665 }
3667 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3668 gchar const */*old_value*/, gchar const */*new_value*/,
3669 bool /*is_interactive*/, gpointer data)
3670 {
3671 GObject *tbl = G_OBJECT(data);
3673 // quit if run by the _changed callbacks
3674 if (g_object_get_data( tbl, "freeze" )) {
3675 return;
3676 }
3678 // in turn, prevent callbacks from responding
3679 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3681 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3682 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3684 GtkAdjustment *adj1,*adj2;
3685 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3686 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3687 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3688 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3690 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3692 char const *openstr = NULL;
3693 openstr = repr->attribute("sodipodi:open");
3694 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3696 if (openstr) {
3697 ege_select_one_action_set_active( ocb, 1 );
3698 } else {
3699 ege_select_one_action_set_active( ocb, 0 );
3700 }
3702 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3703 }
3705 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3706 NULL, /* child_added */
3707 NULL, /* child_removed */
3708 arc_tb_event_attr_changed,
3709 NULL, /* content_changed */
3710 NULL /* order_changed */
3711 };
3714 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3715 {
3716 int n_selected = 0;
3717 Inkscape::XML::Node *repr = NULL;
3719 purge_repr_listener( tbl, tbl );
3721 for (GSList const *items = selection->itemList();
3722 items != NULL;
3723 items = items->next)
3724 {
3725 if (SP_IS_ARC((SPItem *) items->data)) {
3726 n_selected++;
3727 repr = SP_OBJECT_REPR((SPItem *) items->data);
3728 }
3729 }
3731 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3733 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3734 if (n_selected == 0) {
3735 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3736 } else if (n_selected == 1) {
3737 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3738 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3740 if (repr) {
3741 g_object_set_data( tbl, "repr", repr );
3742 Inkscape::GC::anchor(repr);
3743 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3744 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3745 }
3746 } else {
3747 // FIXME: implement averaging of all parameters for multiple selected
3748 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3749 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3750 sp_arctb_sensitivize( tbl, 1, 0 );
3751 }
3752 }
3755 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3756 {
3757 EgeAdjustmentAction* eact = 0;
3760 {
3761 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3762 ege_output_action_set_use_markup( act, TRUE );
3763 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3764 g_object_set_data( holder, "mode_action", act );
3765 }
3767 /* Start */
3768 {
3769 eact = create_adjustment_action( "ArcStartAction",
3770 _("Start"), _("Start:"),
3771 _("The angle (in degrees) from the horizontal to the arc's start point"),
3772 "tools.shapes.arc", "start", 0.0,
3773 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3774 -360.0, 360.0, 1.0, 10.0,
3775 0, 0, 0,
3776 sp_arctb_start_value_changed);
3777 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3778 }
3780 /* End */
3781 {
3782 eact = create_adjustment_action( "ArcEndAction",
3783 _("End"), _("End:"),
3784 _("The angle (in degrees) from the horizontal to the arc's end point"),
3785 "tools.shapes.arc", "end", 0.0,
3786 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3787 -360.0, 360.0, 1.0, 10.0,
3788 0, 0, 0,
3789 sp_arctb_end_value_changed);
3790 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3791 }
3793 /* Segments / Pie checkbox */
3794 {
3795 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3797 GtkTreeIter iter;
3798 gtk_list_store_append( model, &iter );
3799 gtk_list_store_set( model, &iter,
3800 0, _("Closed arc"),
3801 1, _("Switch to segment (closed shape with two radii)"),
3802 2, "circle_closed_arc",
3803 -1 );
3805 gtk_list_store_append( model, &iter );
3806 gtk_list_store_set( model, &iter,
3807 0, _("Open Arc"),
3808 1, _("Switch to arc (unclosed shape)"),
3809 2, "circle_open_arc",
3810 -1 );
3812 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3813 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3814 g_object_set_data( holder, "open_action", act );
3816 ege_select_one_action_set_appearance( act, "full" );
3817 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3818 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3819 ege_select_one_action_set_icon_column( act, 2 );
3820 ege_select_one_action_set_tooltip_column( act, 1 );
3822 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3823 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3824 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3825 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3826 }
3828 /* Make Whole */
3829 {
3830 InkAction* inky = ink_action_new( "ArcResetAction",
3831 _("Make whole"),
3832 _("Make the shape a whole ellipse, not arc or segment"),
3833 "reset_circle",
3834 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3835 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3836 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3837 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3838 g_object_set_data( holder, "make_whole", inky );
3839 }
3841 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3842 // sensitivize make whole and open checkbox
3843 {
3844 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3845 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3846 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3847 }
3850 sigc::connection *connection = new sigc::connection(
3851 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3852 );
3853 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3854 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3855 }
3860 // toggle button callbacks and updaters
3862 //########################
3863 //## Dropper ##
3864 //########################
3866 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3867 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3868 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3869 if ( set_action ) {
3870 if ( gtk_toggle_action_get_active( act ) ) {
3871 gtk_action_set_sensitive( set_action, TRUE );
3872 } else {
3873 gtk_action_set_sensitive( set_action, FALSE );
3874 }
3875 }
3877 spinbutton_defocus(GTK_OBJECT(tbl));
3878 }
3880 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3881 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3882 spinbutton_defocus(GTK_OBJECT(tbl));
3883 }
3886 /**
3887 * Dropper auxiliary toolbar construction and setup.
3888 *
3889 * TODO: Would like to add swatch of current color.
3890 * TODO: Add queue of last 5 or so colors selected with new swatches so that
3891 * can drag and drop places. Will provide a nice mixing palette.
3892 */
3893 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3894 {
3895 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3897 {
3898 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3899 _("Pick alpha"),
3900 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3901 "color_alpha_get",
3902 Inkscape::ICON_SIZE_DECORATION );
3903 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3904 g_object_set_data( holder, "pick_action", act );
3905 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3906 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3907 }
3909 {
3910 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3911 _("Set alpha"),
3912 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3913 "color_alpha_set",
3914 Inkscape::ICON_SIZE_DECORATION );
3915 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3916 g_object_set_data( holder, "set_action", act );
3917 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3918 // make sure it's disabled if we're not picking alpha
3919 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3920 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3921 }
3922 }
3925 //########################
3926 //## Text Toolbox ##
3927 //########################
3928 /*
3929 static void
3930 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3931 {
3932 //Call back for letter sizing spinbutton
3933 }
3935 static void
3936 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3937 {
3938 //Call back for line height spinbutton
3939 }
3941 static void
3942 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3943 {
3944 //Call back for horizontal kerning spinbutton
3945 }
3947 static void
3948 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3949 {
3950 //Call back for vertical kerning spinbutton
3951 }
3953 static void
3954 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3955 {
3956 //Call back for letter rotation spinbutton
3957 }*/
3959 namespace {
3961 bool popdown_visible = false;
3962 bool popdown_hasfocus = false;
3964 void
3965 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
3966 {
3967 SPStyle *query =
3968 sp_style_new (SP_ACTIVE_DOCUMENT);
3970 int result_fontspec =
3971 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
3973 int result_family =
3974 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3976 int result_style =
3977 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3979 int result_numbers =
3980 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3982 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3984 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3985 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3986 {
3987 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3989 if (repr)
3990 {
3991 sp_style_read_from_repr (query, repr);
3992 }
3993 else
3994 {
3995 return;
3996 }
3997 }
3999 if (query->text)
4000 {
4001 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4002 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4003 gtk_entry_set_text (GTK_ENTRY (entry), "");
4005 } else if (query->text->font_specification.value || query->text->font_family.value) {
4007 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4009 // Get the font that corresponds
4010 Glib::ustring familyName;
4012 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4013 if (font) {
4014 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4015 font->Unref();
4016 font = NULL;
4017 }
4019 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4021 Gtk::TreePath path;
4022 try {
4023 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4024 } catch (...) {
4025 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4026 return;
4027 }
4029 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4030 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4032 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4034 gtk_tree_selection_select_path (tselection, path.gobj());
4035 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4037 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4038 }
4040 //Size
4041 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4042 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4043 g_object_set_data (tbl, "size-block", gpointer(1));
4044 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4045 g_object_set_data (tbl, "size-block", gpointer(0));
4046 free (str);
4048 //Anchor
4049 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4050 {
4051 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4052 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4053 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4054 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4055 }
4056 else
4057 {
4058 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4059 {
4060 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4061 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4062 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4063 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4064 }
4065 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4066 {
4067 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4068 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4069 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4070 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4071 }
4072 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4073 {
4074 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4075 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4076 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4077 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4078 }
4079 }
4081 //Style
4082 {
4083 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4085 gboolean active = gtk_toggle_button_get_active (button);
4086 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4088 if (active != check)
4089 {
4090 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4091 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4092 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4093 }
4094 }
4096 {
4097 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4099 gboolean active = gtk_toggle_button_get_active (button);
4100 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4102 if (active != check)
4103 {
4104 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4105 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4106 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4107 }
4108 }
4110 //Orientation
4111 //locking both buttons, changing one affect all group (both)
4112 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4113 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4115 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4116 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4118 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4119 {
4120 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4121 }
4122 else
4123 {
4124 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4125 }
4126 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4127 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4128 }
4130 sp_style_unref(query);
4131 }
4133 void
4134 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4135 {
4136 sp_text_toolbox_selection_changed (selection, tbl);
4137 }
4139 void
4140 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4141 {
4142 sp_text_toolbox_selection_changed (NULL, tbl);
4143 }
4145 void
4146 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
4147 GObject *tbl)
4148 {
4149 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4150 GtkTreeModel *model = 0;
4151 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4152 GtkTreeIter iter;
4153 char *family = 0;
4155 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4156 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4158 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4159 return;
4160 }
4162 gtk_tree_model_get (model, &iter, 0, &family, -1);
4164 if (g_object_get_data (G_OBJECT (selection), "block"))
4165 {
4166 gtk_entry_set_text (GTK_ENTRY (entry), family);
4167 return;
4168 }
4170 gtk_entry_set_text (GTK_ENTRY (entry), family);
4172 SPStyle *query =
4173 sp_style_new (SP_ACTIVE_DOCUMENT);
4175 int result_fontspec =
4176 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4178 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4180 SPCSSAttr *css = sp_repr_css_attr_new ();
4183 // First try to get the font spec from the stored value
4184 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4186 if (fontSpec.empty()) {
4187 // Construct a new font specification if it does not yet exist
4188 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4189 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4190 fontFromStyle->Unref();
4191 }
4193 if (!fontSpec.empty()) {
4194 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4195 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4196 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4197 if (font) {
4198 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4200 // Set all the these just in case they were altered when finding the best
4201 // match for the new family and old style...
4203 gchar c[256];
4205 font->Family(c, 256);
4206 sp_repr_css_set_property (css, "font-family", c);
4208 font->Attribute( "weight", c, 256);
4209 sp_repr_css_set_property (css, "font-weight", c);
4211 font->Attribute("style", c, 256);
4212 sp_repr_css_set_property (css, "font-style", c);
4214 font->Attribute("stretch", c, 256);
4215 sp_repr_css_set_property (css, "font-stretch", c);
4217 font->Attribute("variant", c, 256);
4218 sp_repr_css_set_property (css, "font-variant", c);
4220 font->Unref();
4221 }
4222 }
4223 }
4225 // If querying returned nothing, set the default style of the tool (for new texts)
4226 if (result_fontspec == QUERY_STYLE_NOTHING)
4227 {
4228 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4229 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4230 }
4231 else
4232 {
4233 sp_desktop_set_style (desktop, css, true, true);
4234 }
4236 sp_style_unref(query);
4238 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4239 _("Text: Change font family"));
4240 sp_repr_css_attr_unref (css);
4241 free (family);
4242 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4244 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4245 }
4247 void
4248 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
4249 GObject *tbl)
4250 {
4251 const char *family = gtk_entry_get_text (entry);
4253 try {
4254 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4255 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4256 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4257 gtk_tree_selection_select_path (selection, path.gobj());
4258 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4259 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4260 } catch (...) {
4261 if (family && strlen (family))
4262 {
4263 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4264 }
4265 }
4266 }
4268 void
4269 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
4270 gpointer data)
4271 {
4272 if (g_object_get_data (G_OBJECT (button), "block")) return;
4273 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4274 int prop = GPOINTER_TO_INT(data);
4276 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4277 SPCSSAttr *css = sp_repr_css_attr_new ();
4279 switch (prop)
4280 {
4281 case 0:
4282 {
4283 sp_repr_css_set_property (css, "text-anchor", "start");
4284 sp_repr_css_set_property (css, "text-align", "start");
4285 break;
4286 }
4287 case 1:
4288 {
4289 sp_repr_css_set_property (css, "text-anchor", "middle");
4290 sp_repr_css_set_property (css, "text-align", "center");
4291 break;
4292 }
4294 case 2:
4295 {
4296 sp_repr_css_set_property (css, "text-anchor", "end");
4297 sp_repr_css_set_property (css, "text-align", "end");
4298 break;
4299 }
4301 case 3:
4302 {
4303 sp_repr_css_set_property (css, "text-anchor", "start");
4304 sp_repr_css_set_property (css, "text-align", "justify");
4305 break;
4306 }
4307 }
4309 SPStyle *query =
4310 sp_style_new (SP_ACTIVE_DOCUMENT);
4311 int result_numbers =
4312 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4314 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4315 if (result_numbers == QUERY_STYLE_NOTHING)
4316 {
4317 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4318 }
4320 sp_style_unref(query);
4322 sp_desktop_set_style (desktop, css, true, true);
4323 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4324 _("Text: Change alignment"));
4325 sp_repr_css_attr_unref (css);
4327 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4328 }
4330 void
4331 sp_text_toolbox_style_toggled (GtkToggleButton *button,
4332 gpointer data)
4333 {
4334 if (g_object_get_data (G_OBJECT (button), "block")) return;
4336 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4337 SPCSSAttr *css = sp_repr_css_attr_new ();
4338 int prop = GPOINTER_TO_INT(data);
4339 bool active = gtk_toggle_button_get_active (button);
4341 SPStyle *query =
4342 sp_style_new (SP_ACTIVE_DOCUMENT);
4344 int result_fontspec =
4345 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4347 int result_family =
4348 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4350 int result_style =
4351 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4353 int result_numbers =
4354 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4356 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4357 Glib::ustring newFontSpec = "";
4359 if (fontSpec.empty()) {
4360 // Construct a new font specification if it does not yet exist
4361 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4362 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4363 fontFromStyle->Unref();
4364 }
4366 switch (prop)
4367 {
4368 case 0:
4369 {
4370 if (!fontSpec.empty()) {
4371 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4372 }
4373 if (fontSpec != newFontSpec) {
4374 // Don't even set the bold if the font didn't exist on the system
4375 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4376 }
4377 break;
4378 }
4380 case 1:
4381 {
4382 if (!fontSpec.empty()) {
4383 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4384 }
4385 if (fontSpec != newFontSpec) {
4386 // Don't even set the italic if the font didn't exist on the system
4387 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4388 }
4389 break;
4390 }
4391 }
4393 if (!newFontSpec.empty()) {
4394 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4395 }
4397 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4398 if (result_fontspec == QUERY_STYLE_NOTHING)
4399 {
4400 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4401 }
4403 sp_style_unref(query);
4405 sp_desktop_set_style (desktop, css, true, true);
4406 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4407 _("Text: Change font style"));
4408 sp_repr_css_attr_unref (css);
4410 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4411 }
4413 void
4414 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
4415 gpointer data)
4416 {
4417 if (g_object_get_data (G_OBJECT (button), "block")) {
4418 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4419 return;
4420 }
4422 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4423 SPCSSAttr *css = sp_repr_css_attr_new ();
4424 int prop = GPOINTER_TO_INT(data);
4426 switch (prop)
4427 {
4428 case 0:
4429 {
4430 sp_repr_css_set_property (css, "writing-mode", "lr");
4431 break;
4432 }
4434 case 1:
4435 {
4436 sp_repr_css_set_property (css, "writing-mode", "tb");
4437 break;
4438 }
4439 }
4441 SPStyle *query =
4442 sp_style_new (SP_ACTIVE_DOCUMENT);
4443 int result_numbers =
4444 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4446 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4447 if (result_numbers == QUERY_STYLE_NOTHING)
4448 {
4449 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4450 }
4452 sp_desktop_set_style (desktop, css, true, true);
4453 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4454 _("Text: Change orientation"));
4455 sp_repr_css_attr_unref (css);
4457 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4458 }
4460 gboolean
4461 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4462 {
4463 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4464 if (!desktop) return FALSE;
4466 switch (get_group0_keyval (event)) {
4467 case GDK_Escape: // defocus
4468 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4469 sp_text_toolbox_selection_changed (NULL, tbl); // update
4470 return TRUE; // I consumed the event
4471 break;
4472 }
4473 return FALSE;
4474 }
4476 gboolean
4477 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4478 {
4479 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4480 if (!desktop) return FALSE;
4482 switch (get_group0_keyval (event)) {
4483 case GDK_KP_Enter:
4484 case GDK_Return:
4485 case GDK_Escape: // defocus
4486 gtk_widget_hide (w);
4487 popdown_visible = false;
4488 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4489 return TRUE; // I consumed the event
4490 break;
4491 case GDK_w:
4492 case GDK_W:
4493 if (event->state & GDK_CONTROL_MASK) {
4494 gtk_widget_hide (w);
4495 popdown_visible = false;
4496 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4497 return TRUE; // I consumed the event
4498 }
4499 break;
4500 }
4501 return FALSE;
4502 }
4505 void
4506 sp_text_toolbox_size_changed (GtkComboBox *cbox,
4507 GObject *tbl)
4508 {
4509 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4511 if (g_object_get_data (tbl, "size-block")) return;
4513 // If this is not from selecting a size in the list (in which case get_active will give the
4514 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4515 // process this event. This fixes GTK's stupid insistence on sending an activate change every
4516 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4517 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4518 return;
4520 gchar *endptr;
4521 gdouble value = -1;
4522 char *text = gtk_combo_box_get_active_text (cbox);
4523 if (text) {
4524 value = g_strtod (text, &endptr);
4525 if (endptr == text) // conversion failed, non-numeric input
4526 value = -1;
4527 free (text);
4528 }
4529 if (value <= 0) {
4530 return; // could not parse value
4531 }
4533 SPCSSAttr *css = sp_repr_css_attr_new ();
4534 Inkscape::CSSOStringStream osfs;
4535 osfs << value;
4536 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4538 SPStyle *query =
4539 sp_style_new (SP_ACTIVE_DOCUMENT);
4540 int result_numbers =
4541 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4543 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4544 if (result_numbers == QUERY_STYLE_NOTHING)
4545 {
4546 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4547 }
4549 sp_style_unref(query);
4551 sp_desktop_set_style (desktop, css, true, true);
4552 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4553 _("Text: Change font size"));
4554 sp_repr_css_attr_unref (css);
4556 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4557 }
4559 gboolean
4560 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus *event, GObject *tbl)
4561 {
4562 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4563 if (!desktop) return FALSE;
4565 if (!g_object_get_data (tbl, "esc-pressed")) {
4566 g_object_set_data (tbl, "enter-pressed", gpointer(1));
4567 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4568 sp_text_toolbox_size_changed (cbox, tbl);
4569 g_object_set_data (tbl, "enter-pressed", gpointer(0));
4570 }
4571 return FALSE; // I consumed the event
4572 }
4575 gboolean
4576 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4577 {
4578 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4579 if (!desktop) return FALSE;
4581 switch (get_group0_keyval (event)) {
4582 case GDK_Escape: // defocus
4583 g_object_set_data (tbl, "esc-pressed", gpointer(1));
4584 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4585 g_object_set_data (tbl, "esc-pressed", gpointer(0));
4586 return TRUE; // I consumed the event
4587 break;
4588 case GDK_Return: // defocus
4589 case GDK_KP_Enter:
4590 g_object_set_data (tbl, "enter-pressed", gpointer(1));
4591 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4592 sp_text_toolbox_size_changed (cbox, tbl);
4593 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4594 g_object_set_data (tbl, "enter-pressed", gpointer(0));
4595 return TRUE; // I consumed the event
4596 break;
4597 }
4598 return FALSE;
4599 }
4601 void
4602 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
4603 GObject *tbl)
4604 {
4605 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4606 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4607 int x, y;
4609 if (!popdown_visible)
4610 {
4611 gdk_window_get_origin (widget->window, &x, &y);
4612 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4613 gtk_widget_show_all (popdown);
4614 //sp_transientize (popdown);
4616 gdk_pointer_grab (widget->window, TRUE,
4617 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4618 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4619 GDK_POINTER_MOTION_MASK),
4620 NULL, NULL, GDK_CURRENT_TIME);
4622 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4624 popdown_visible = true;
4625 }
4626 else
4627 {
4628 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4629 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4630 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4631 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4632 gtk_widget_hide (popdown);
4633 popdown_visible = false;
4634 }
4635 }
4637 gboolean
4638 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
4639 GdkEventFocus */*event*/,
4640 GObject */*tbl*/)
4641 {
4642 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4643 return FALSE;
4644 }
4646 gboolean
4647 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
4648 GdkEventFocus */*event*/,
4649 GObject */*tbl*/)
4650 {
4651 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4653 if (popdown_hasfocus) {
4654 gtk_widget_hide (popdown);
4655 popdown_hasfocus = false;
4656 popdown_visible = false;
4657 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4658 return TRUE;
4659 }
4660 return FALSE;
4661 }
4663 gboolean
4664 sp_text_toolbox_popdown_focus_in (GtkWidget *popdown,
4665 GdkEventFocus */*event*/,
4666 GObject */*tbl*/)
4667 {
4668 popdown_hasfocus = true;
4669 return TRUE;
4670 }
4673 void
4674 cell_data_func (GtkTreeViewColumn */*column*/,
4675 GtkCellRenderer *cell,
4676 GtkTreeModel *tree_model,
4677 GtkTreeIter *iter,
4678 gpointer /*data*/)
4679 {
4680 char *family,
4681 *family_escaped,
4682 *sample_escaped;
4684 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4686 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4688 family_escaped = g_markup_escape_text (family, -1);
4689 sample_escaped = g_markup_escape_text (sample, -1);
4691 std::stringstream markup;
4692 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4693 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4695 free (family);
4696 free (family_escaped);
4697 free (sample_escaped);
4698 }
4700 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4701 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4702 if (completion) {
4703 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4704 g_object_unref (completion);
4705 }
4706 }
4708 GtkWidget*
4709 sp_text_toolbox_new (SPDesktop *desktop)
4710 {
4711 GtkWidget *tbl = gtk_hbox_new (FALSE, 0);
4713 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4714 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4716 GtkTooltips *tt = gtk_tooltips_new();
4717 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4719 ////////////Family
4720 //Window
4721 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4722 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4724 //Entry
4725 GtkWidget *entry = gtk_entry_new ();
4726 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4727 GtkEntryCompletion *completion = gtk_entry_completion_new ();
4728 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4729 gtk_entry_completion_set_text_column (completion, 0);
4730 gtk_entry_completion_set_minimum_key_length (completion, 1);
4731 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4732 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4733 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4734 aux_toolbox_space (tbl, 1);
4735 gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4736 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4738 //Button
4739 GtkWidget *button = gtk_button_new ();
4740 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4741 gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4743 //Popdown
4744 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
4745 GtkWidget *treeview = gtk_tree_view_new ();
4747 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
4748 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
4749 gtk_tree_view_column_pack_start (column, cell, FALSE);
4750 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4751 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4752 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4754 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4755 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4756 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4758 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4760 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4761 gtk_container_add (GTK_CONTAINER (sw), treeview);
4763 gtk_container_add (GTK_CONTAINER (window), sw);
4764 gtk_widget_set_size_request (window, 300, 450);
4766 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4767 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4768 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4770 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4772 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4773 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
4774 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4776 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4777 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4779 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4780 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4781 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4782 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4783 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4785 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4786 aux_toolbox_space (tbl, 1);
4787 GtkWidget *box = gtk_event_box_new ();
4788 gtk_container_add (GTK_CONTAINER (box), image);
4789 gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4790 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4791 GtkTooltips *tooltips = gtk_tooltips_new ();
4792 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4793 gtk_widget_hide (GTK_WIDGET (box));
4794 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4796 ////////////Size
4797 const char *sizes[] = {
4798 "4", "6", "8", "9", "10", "11", "12", "13", "14",
4799 "16", "18", "20", "22", "24", "28",
4800 "32", "36", "40", "48", "56", "64", "72", "144"
4801 };
4803 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4804 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4805 gtk_widget_set_size_request (cbox, 80, -1);
4806 aux_toolbox_space (tbl, 1);
4807 gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4808 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4809 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4810 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
4811 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
4813 //spacer
4814 aux_toolbox_space (tbl, 4);
4815 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4817 ////////////Text anchor
4818 GtkWidget *group = gtk_radio_button_new (NULL);
4819 GtkWidget *row = gtk_hbox_new (FALSE, 4);
4820 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4822 // left
4823 GtkWidget *rbutton = group;
4824 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4825 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4826 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4828 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4829 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
4830 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4831 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4833 // center
4834 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4835 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4836 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4837 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4839 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4840 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
4841 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4842 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4844 // right
4845 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4846 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4847 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4848 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4850 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4851 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
4852 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4853 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4855 // fill
4856 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4857 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4858 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4859 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4861 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4862 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
4863 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4864 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4866 aux_toolbox_space (tbl, 1);
4867 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4869 //spacer
4870 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4872 ////////////Text style
4873 row = gtk_hbox_new (FALSE, 4);
4875 // bold
4876 rbutton = gtk_toggle_button_new ();
4877 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4878 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4879 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4880 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4882 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4883 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
4884 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4886 // italic
4887 rbutton = gtk_toggle_button_new ();
4888 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4889 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4890 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4891 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4893 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4894 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
4895 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4897 aux_toolbox_space (tbl, 1);
4898 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4900 //spacer
4901 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4903 ////////////Text orientation
4904 group = gtk_radio_button_new (NULL);
4905 row = gtk_hbox_new (FALSE, 4);
4906 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4908 // horizontal
4909 rbutton = group;
4910 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4911 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4912 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4913 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4915 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4916 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4917 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4919 // vertical
4920 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4921 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4922 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4923 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4924 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4926 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4927 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
4928 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4929 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4932 //watch selection
4933 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4935 sigc::connection *c_selection_changed =
4936 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4937 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4938 pool->add_connection ("selection-changed", c_selection_changed);
4940 sigc::connection *c_selection_modified =
4941 new sigc::connection (sp_desktop_selection (desktop)->connectModified
4942 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4943 pool->add_connection ("selection-modified", c_selection_modified);
4945 sigc::connection *c_subselection_changed =
4946 new sigc::connection (desktop->connectToolSubselectionChanged
4947 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4948 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4950 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4953 gtk_widget_show_all (tbl);
4954 return tbl;
4956 } // end of sp_text_toolbox_new()
4958 }//<unnamed> namespace
4961 //#########################
4962 //## Connector ##
4963 //#########################
4965 static void sp_connector_path_set_avoid(void)
4966 {
4967 cc_selection_set_avoid(true);
4968 }
4971 static void sp_connector_path_set_ignore(void)
4972 {
4973 cc_selection_set_avoid(false);
4974 }
4978 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4979 {
4980 // quit if run by the _changed callbacks
4981 if (g_object_get_data( tbl, "freeze" )) {
4982 return;
4983 }
4985 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4986 SPDocument *doc = sp_desktop_document(desktop);
4988 if (!sp_document_get_undo_sensitive(doc))
4989 {
4990 return;
4991 }
4993 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4995 if ( repr->attribute("inkscape:connector-spacing") ) {
4996 gdouble priorValue = gtk_adjustment_get_value(adj);
4997 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4998 if ( priorValue == gtk_adjustment_get_value(adj) ) {
4999 return;
5000 }
5001 } else if ( adj->value == defaultConnSpacing ) {
5002 return;
5003 }
5005 // in turn, prevent callbacks from responding
5006 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5008 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5009 SP_OBJECT(desktop->namedview)->updateRepr();
5011 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5012 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5013 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5014 NR::Matrix m = NR::identity();
5015 avoid_item_move(&m, item);
5016 }
5018 if (items) {
5019 g_slist_free(items);
5020 }
5022 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5023 _("Change connector spacing"));
5025 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5027 spinbutton_defocus(GTK_OBJECT(tbl));
5028 }
5030 static void sp_connector_graph_layout(void)
5031 {
5032 if (!SP_ACTIVE_DESKTOP) return;
5034 // hack for clones, see comment in align-and-distribute.cpp
5035 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5036 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5038 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5040 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5042 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5043 }
5045 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5046 {
5047 if ( gtk_toggle_action_get_active( act ) ) {
5048 prefs_set_string_attribute("tools.connector", "directedlayout",
5049 "true");
5050 } else {
5051 prefs_set_string_attribute("tools.connector", "directedlayout",
5052 "false");
5053 }
5054 }
5056 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5057 {
5058 if ( gtk_toggle_action_get_active( act ) ) {
5059 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5060 "true");
5061 } else {
5062 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5063 "false");
5064 }
5065 }
5068 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5069 {
5070 prefs_set_double_attribute("tools.connector", "length", adj->value);
5071 spinbutton_defocus(GTK_OBJECT(tbl));
5072 }
5074 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5075 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5076 bool /*is_interactive*/, gpointer data)
5077 {
5078 GtkWidget *tbl = GTK_WIDGET(data);
5080 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5081 return;
5082 }
5083 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5084 return;
5085 }
5087 GtkAdjustment *adj = (GtkAdjustment*)
5088 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5089 gdouble spacing = defaultConnSpacing;
5090 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5092 gtk_adjustment_set_value(adj, spacing);
5093 }
5096 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5097 NULL, /* child_added */
5098 NULL, /* child_removed */
5099 connector_tb_event_attr_changed,
5100 NULL, /* content_changed */
5101 NULL /* order_changed */
5102 };
5105 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5106 {
5107 {
5108 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5109 _("Avoid"),
5110 _("Make connectors avoid selected objects"),
5111 "connector_avoid",
5112 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5113 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5114 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5115 }
5117 {
5118 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5119 _("Ignore"),
5120 _("Make connectors ignore selected objects"),
5121 "connector_ignore",
5122 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5123 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5124 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5125 }
5127 EgeAdjustmentAction* eact = 0;
5129 // Spacing spinbox
5130 eact = create_adjustment_action( "ConnectorSpacingAction",
5131 _("Connector Spacing"), _("Spacing:"),
5132 _("The amount of space left around objects by auto-routing connectors"),
5133 "tools.connector", "spacing", defaultConnSpacing,
5134 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5135 0, 100, 1.0, 10.0,
5136 0, 0, 0,
5137 connector_spacing_changed, 1, 0 );
5138 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5140 // Graph (connector network) layout
5141 {
5142 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5143 _("Graph"),
5144 _("Nicely arrange selected connector network"),
5145 "graph_layout",
5146 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5147 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5148 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5149 }
5151 // Default connector length spinbox
5152 eact = create_adjustment_action( "ConnectorLengthAction",
5153 _("Connector Length"), _("Length:"),
5154 _("Ideal length for connectors when layout is applied"),
5155 "tools.connector", "length", 100,
5156 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5157 10, 1000, 10.0, 100.0,
5158 0, 0, 0,
5159 connector_length_changed, 1, 0 );
5160 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5163 // Directed edges toggle button
5164 {
5165 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5166 _("Downwards"),
5167 _("Make connectors with end-markers (arrows) point downwards"),
5168 "directed_graph",
5169 Inkscape::ICON_SIZE_DECORATION );
5170 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5172 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5173 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5174 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5176 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5177 }
5179 // Avoid overlaps toggle button
5180 {
5181 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5182 _("Remove overlaps"),
5183 _("Do not allow overlapping shapes"),
5184 "remove_overlaps",
5185 Inkscape::ICON_SIZE_DECORATION );
5186 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5188 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5189 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5190 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5192 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5193 }
5195 // Code to watch for changes to the connector-spacing attribute in
5196 // the XML.
5197 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5198 g_assert(repr != NULL);
5200 purge_repr_listener( holder, holder );
5202 if (repr) {
5203 g_object_set_data( holder, "repr", repr );
5204 Inkscape::GC::anchor(repr);
5205 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5206 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5207 }
5208 } // end of sp_connector_toolbox_prep()
5211 //#########################
5212 //## Paintbucket ##
5213 //#########################
5215 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5216 {
5217 gint channels = ege_select_one_action_get_active( act );
5218 flood_channels_set_channels( channels );
5219 }
5221 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5222 {
5223 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5224 }
5226 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5227 {
5228 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5229 }
5231 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5232 {
5233 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5234 SPUnit const *unit = tracker->getActiveUnit();
5236 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5238 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5239 }
5241 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5242 {
5243 // FIXME: make defaults settable via Inkscape Options
5244 struct KeyValue {
5245 char const *key;
5246 double value;
5247 } const key_values[] = {
5248 {"threshold", 15},
5249 {"offset", 0.0}
5250 };
5252 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5253 KeyValue const &kv = key_values[i];
5254 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5255 if ( adj ) {
5256 gtk_adjustment_set_value(adj, kv.value);
5257 }
5258 }
5260 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5261 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5262 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5263 ege_select_one_action_set_active( autogap_action, 0 );
5264 }
5266 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5267 {
5268 EgeAdjustmentAction* eact = 0;
5270 {
5271 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5273 GList* items = 0;
5274 gint count = 0;
5275 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5276 {
5277 GtkTreeIter iter;
5278 gtk_list_store_append( model, &iter );
5279 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5280 count++;
5281 }
5282 g_list_free( items );
5283 items = 0;
5284 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5285 g_object_set( act1, "short_label", _("Fill by:"), NULL );
5286 ege_select_one_action_set_appearance( act1, "compact" );
5287 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5288 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5289 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5290 g_object_set_data( holder, "channels_action", act1 );
5291 }
5293 // Spacing spinbox
5294 {
5295 eact = create_adjustment_action(
5296 "ThresholdAction",
5297 _("Fill Threshold"), _("Threshold:"),
5298 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5299 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5300 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5301 0, 0, 0,
5302 paintbucket_threshold_changed, 1, 0 );
5304 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5305 }
5307 // Create the units menu.
5308 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5309 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5310 if (stored_unit)
5311 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5312 g_object_set_data( holder, "tracker", tracker );
5313 {
5314 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5315 gtk_action_group_add_action( mainActions, act );
5316 }
5318 // Offset spinbox
5319 {
5320 eact = create_adjustment_action(
5321 "OffsetAction",
5322 _("Grow/shrink by"), _("Grow/shrink by:"),
5323 _("The amount to grow (positive) or shrink (negative) the created fill path"),
5324 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5325 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5326 0, 0, 0,
5327 paintbucket_offset_changed, 1, 2);
5328 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5330 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5331 }
5333 /* Auto Gap */
5334 {
5335 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5337 GList* items = 0;
5338 gint count = 0;
5339 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5340 {
5341 GtkTreeIter iter;
5342 gtk_list_store_append( model, &iter );
5343 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5344 count++;
5345 }
5346 g_list_free( items );
5347 items = 0;
5348 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5349 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5350 ege_select_one_action_set_appearance( act2, "compact" );
5351 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5352 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5353 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5354 g_object_set_data( holder, "autogap_action", act2 );
5355 }
5357 /* Reset */
5358 {
5359 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5360 _("Defaults"),
5361 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5362 GTK_STOCK_CLEAR );
5363 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5364 gtk_action_group_add_action( mainActions, act );
5365 gtk_action_set_sensitive( act, TRUE );
5366 }
5368 }
5370 /*
5371 Local Variables:
5372 mode:c++
5373 c-file-style:"stroustrup"
5374 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5375 indent-tabs-mode:nil
5376 fill-column:99
5377 End:
5378 */
5379 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :