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