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 if (!strcmp(name, "inkscape:randomized")) {
1721 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1722 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1723 } else if (!strcmp(name, "inkscape:rounded")) {
1724 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1725 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1726 } else if (!strcmp(name, "inkscape:flatsided")) {
1727 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1728 char const *flatsides = repr->attribute("inkscape:flatsided");
1729 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1730 if ( flatsides && !strcmp(flatsides,"false") ) {
1731 ege_select_one_action_set_active( flat_action, 1 );
1732 gtk_action_set_sensitive( prop_action, TRUE );
1733 } else {
1734 ege_select_one_action_set_active( flat_action, 0 );
1735 gtk_action_set_sensitive( prop_action, FALSE );
1736 }
1737 } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
1738 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1739 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1740 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1741 if (r2 < r1) {
1742 gtk_adjustment_set_value(adj, r2/r1);
1743 } else {
1744 gtk_adjustment_set_value(adj, r1/r2);
1745 }
1746 } else if (!strcmp(name, "sodipodi:sides")) {
1747 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1748 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1749 }
1751 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1752 }
1755 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1756 {
1757 NULL, /* child_added */
1758 NULL, /* child_removed */
1759 star_tb_event_attr_changed,
1760 NULL, /* content_changed */
1761 NULL /* order_changed */
1762 };
1765 /**
1766 * \param selection Should not be NULL.
1767 */
1768 static void
1769 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1770 {
1771 int n_selected = 0;
1772 Inkscape::XML::Node *repr = NULL;
1774 purge_repr_listener( tbl, tbl );
1776 for (GSList const *items = selection->itemList();
1777 items != NULL;
1778 items = items->next)
1779 {
1780 if (SP_IS_STAR((SPItem *) items->data)) {
1781 n_selected++;
1782 repr = SP_OBJECT_REPR((SPItem *) items->data);
1783 }
1784 }
1786 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1788 if (n_selected == 0) {
1789 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1790 } else if (n_selected == 1) {
1791 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1793 if (repr) {
1794 g_object_set_data( tbl, "repr", repr );
1795 Inkscape::GC::anchor(repr);
1796 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1797 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1798 }
1799 } else {
1800 // FIXME: implement averaging of all parameters for multiple selected stars
1801 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1802 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1803 }
1804 }
1807 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1808 {
1809 // FIXME: in this and all other _default functions, set some flag telling the value_changed
1810 // callbacks to lump all the changes for all selected objects in one undo step
1812 GtkAdjustment *adj = 0;
1814 // fixme: make settable in prefs!
1815 gint mag = 5;
1816 gdouble prop = 0.5;
1817 gboolean flat = FALSE;
1818 gdouble randomized = 0;
1819 gdouble rounded = 0;
1821 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1822 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1824 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1825 gtk_action_set_sensitive( sb2, !flat );
1827 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1828 gtk_adjustment_set_value(adj, mag);
1829 gtk_adjustment_value_changed(adj);
1831 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1832 gtk_adjustment_set_value(adj, prop);
1833 gtk_adjustment_value_changed(adj);
1835 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1836 gtk_adjustment_set_value(adj, rounded);
1837 gtk_adjustment_value_changed(adj);
1839 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1840 gtk_adjustment_set_value(adj, randomized);
1841 gtk_adjustment_value_changed(adj);
1842 }
1845 void
1846 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1847 {
1848 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1849 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1850 GtkWidget *l = gtk_label_new(NULL);
1851 gtk_label_set_markup(GTK_LABEL(l), title);
1852 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1853 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1854 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1855 }
1858 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1859 {
1860 {
1861 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1862 ege_output_action_set_use_markup( act, TRUE );
1863 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1864 g_object_set_data( holder, "mode_action", act );
1865 }
1867 {
1868 EgeAdjustmentAction* eact = 0;
1869 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1870 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1872 /* Flatsided checkbox */
1873 {
1874 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1876 GtkTreeIter iter;
1877 gtk_list_store_append( model, &iter );
1878 gtk_list_store_set( model, &iter,
1879 0, _("Polygon"),
1880 1, _("Regular polygon (with one handle) instead of a star"),
1881 2, "star_flat",
1882 -1 );
1884 gtk_list_store_append( model, &iter );
1885 gtk_list_store_set( model, &iter,
1886 0, _("Star"),
1887 1, _("Star instead of a regular polygon (with one handle)"),
1888 2, "star_angled",
1889 -1 );
1891 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
1892 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1893 g_object_set_data( holder, "flat_action", act );
1895 ege_select_one_action_set_appearance( act, "full" );
1896 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1897 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1898 ege_select_one_action_set_icon_column( act, 2 );
1899 ege_select_one_action_set_tooltip_column( act, 1 );
1901 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1902 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1903 }
1905 /* Magnitude */
1906 {
1907 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1908 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1909 eact = create_adjustment_action( "MagnitudeAction",
1910 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
1911 "tools.shapes.star", "magnitude", 3,
1912 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1913 3, 1024, 1, 5,
1914 labels, values, G_N_ELEMENTS(labels),
1915 sp_stb_magnitude_value_changed,
1916 1.0, 0 );
1917 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1918 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1919 }
1921 /* Spoke ratio */
1922 {
1923 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1924 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1925 eact = create_adjustment_action( "SpokeAction",
1926 _("Spoke ratio"), _("Spoke ratio:"),
1927 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1928 // Base radius is the same for the closest handle.
1929 _("Base radius to tip radius ratio"),
1930 "tools.shapes.star", "proportion", 0.5,
1931 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1932 0.01, 1.0, 0.01, 0.1,
1933 labels, values, G_N_ELEMENTS(labels),
1934 sp_stb_proportion_value_changed );
1935 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1936 g_object_set_data( holder, "prop_action", eact );
1937 }
1939 if ( !isFlatSided ) {
1940 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1941 } else {
1942 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1943 }
1945 /* Roundedness */
1946 {
1947 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1948 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1949 eact = create_adjustment_action( "RoundednessAction",
1950 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1951 "tools.shapes.star", "rounded", 0.0,
1952 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1953 -10.0, 10.0, 0.01, 0.1,
1954 labels, values, G_N_ELEMENTS(labels),
1955 sp_stb_rounded_value_changed );
1956 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1957 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1958 }
1960 /* Randomization */
1961 {
1962 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1963 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1964 eact = create_adjustment_action( "RandomizationAction",
1965 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
1966 "tools.shapes.star", "randomized", 0.0,
1967 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1968 -10.0, 10.0, 0.001, 0.01,
1969 labels, values, G_N_ELEMENTS(labels),
1970 sp_stb_randomized_value_changed, 0.1, 3 );
1971 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1972 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1973 }
1974 }
1976 {
1977 /* Reset */
1978 {
1979 GtkAction* act = gtk_action_new( "StarResetAction",
1980 _("Defaults"),
1981 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
1982 GTK_STOCK_CLEAR );
1983 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
1984 gtk_action_group_add_action( mainActions, act );
1985 gtk_action_set_sensitive( act, TRUE );
1986 }
1987 }
1989 sigc::connection *connection = new sigc::connection(
1990 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
1991 );
1992 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1993 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1994 }
1997 //########################
1998 //## Rect ##
1999 //########################
2001 static void sp_rtb_sensitivize( GObject *tbl )
2002 {
2003 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2004 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2005 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2007 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2008 gtk_action_set_sensitive( not_rounded, FALSE );
2009 } else {
2010 gtk_action_set_sensitive( not_rounded, TRUE );
2011 }
2012 }
2015 static void
2016 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2017 void (*setter)(SPRect *, gdouble))
2018 {
2019 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2021 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2022 SPUnit const *unit = tracker->getActiveUnit();
2024 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2025 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2026 }
2028 // quit if run by the attr_changed listener
2029 if (g_object_get_data( tbl, "freeze" )) {
2030 return;
2031 }
2033 // in turn, prevent listener from responding
2034 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2036 bool modmade = false;
2037 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2038 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2039 if (SP_IS_RECT(items->data)) {
2040 if (adj->value != 0) {
2041 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2042 } else {
2043 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2044 }
2045 modmade = true;
2046 }
2047 }
2049 sp_rtb_sensitivize( tbl );
2051 if (modmade) {
2052 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2053 _("Change rectangle"));
2054 }
2056 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2057 }
2059 static void
2060 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2061 {
2062 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2063 }
2065 static void
2066 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2067 {
2068 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2069 }
2071 static void
2072 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2073 {
2074 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2075 }
2077 static void
2078 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2079 {
2080 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2081 }
2085 static void
2086 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2087 {
2088 GtkAdjustment *adj = 0;
2090 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2091 gtk_adjustment_set_value(adj, 0.0);
2092 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2093 gtk_adjustment_value_changed(adj);
2095 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2096 gtk_adjustment_set_value(adj, 0.0);
2097 gtk_adjustment_value_changed(adj);
2099 sp_rtb_sensitivize( obj );
2100 }
2102 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2103 gchar const */*old_value*/, gchar const */*new_value*/,
2104 bool /*is_interactive*/, gpointer data)
2105 {
2106 GObject *tbl = G_OBJECT(data);
2108 // quit if run by the _changed callbacks
2109 if (g_object_get_data( tbl, "freeze" )) {
2110 return;
2111 }
2113 // in turn, prevent callbacks from responding
2114 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2116 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2117 SPUnit const *unit = tracker->getActiveUnit();
2119 gpointer item = g_object_get_data( tbl, "item" );
2120 if (item && SP_IS_RECT(item)) {
2121 {
2122 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2123 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2124 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2125 }
2127 {
2128 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2129 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2130 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2131 }
2133 {
2134 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2135 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2136 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2137 }
2139 {
2140 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2141 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2142 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2143 }
2144 }
2146 sp_rtb_sensitivize( tbl );
2148 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2149 }
2152 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2153 NULL, /* child_added */
2154 NULL, /* child_removed */
2155 rect_tb_event_attr_changed,
2156 NULL, /* content_changed */
2157 NULL /* order_changed */
2158 };
2160 /**
2161 * \param selection should not be NULL.
2162 */
2163 static void
2164 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2165 {
2166 int n_selected = 0;
2167 Inkscape::XML::Node *repr = NULL;
2168 SPItem *item = NULL;
2170 if ( g_object_get_data( tbl, "repr" ) ) {
2171 g_object_set_data( tbl, "item", NULL );
2172 }
2173 purge_repr_listener( tbl, tbl );
2175 for (GSList const *items = selection->itemList();
2176 items != NULL;
2177 items = items->next) {
2178 if (SP_IS_RECT((SPItem *) items->data)) {
2179 n_selected++;
2180 item = (SPItem *) items->data;
2181 repr = SP_OBJECT_REPR(item);
2182 }
2183 }
2185 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2187 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2189 if (n_selected == 0) {
2190 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2192 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2193 gtk_action_set_sensitive(w, FALSE);
2194 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2195 gtk_action_set_sensitive(h, FALSE);
2197 } else if (n_selected == 1) {
2198 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2199 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2201 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2202 gtk_action_set_sensitive(w, TRUE);
2203 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2204 gtk_action_set_sensitive(h, TRUE);
2206 if (repr) {
2207 g_object_set_data( tbl, "repr", repr );
2208 g_object_set_data( tbl, "item", item );
2209 Inkscape::GC::anchor(repr);
2210 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2211 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2212 }
2213 } else {
2214 // FIXME: implement averaging of all parameters for multiple selected
2215 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2216 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2217 sp_rtb_sensitivize( tbl );
2218 }
2219 }
2222 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2223 {
2224 EgeAdjustmentAction* eact = 0;
2226 {
2227 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2228 ege_output_action_set_use_markup( act, TRUE );
2229 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2230 g_object_set_data( holder, "mode_action", act );
2231 }
2233 // rx/ry units menu: create
2234 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2235 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2236 // fixme: add % meaning per cent of the width/height
2237 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2238 g_object_set_data( holder, "tracker", tracker );
2240 /* W */
2241 {
2242 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2243 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2244 eact = create_adjustment_action( "RectWidthAction",
2245 _("Width"), _("W:"), _("Width of rectangle"),
2246 "tools.shapes.rect", "width", 0,
2247 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2248 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2249 labels, values, G_N_ELEMENTS(labels),
2250 sp_rtb_width_value_changed );
2251 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2252 g_object_set_data( holder, "width_action", eact );
2253 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2254 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2255 }
2257 /* H */
2258 {
2259 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2260 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2261 eact = create_adjustment_action( "RectHeightAction",
2262 _("Height"), _("H:"), _("Height of rectangle"),
2263 "tools.shapes.rect", "height", 0,
2264 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2265 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2266 labels, values, G_N_ELEMENTS(labels),
2267 sp_rtb_height_value_changed );
2268 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2269 g_object_set_data( holder, "height_action", eact );
2270 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2271 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2272 }
2274 /* rx */
2275 {
2276 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2277 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2278 eact = create_adjustment_action( "RadiusXAction",
2279 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2280 "tools.shapes.rect", "rx", 0,
2281 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2282 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2283 labels, values, G_N_ELEMENTS(labels),
2284 sp_rtb_rx_value_changed);
2285 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2286 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2287 }
2289 /* ry */
2290 {
2291 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2292 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2293 eact = create_adjustment_action( "RadiusYAction",
2294 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2295 "tools.shapes.rect", "ry", 0,
2296 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2297 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2298 labels, values, G_N_ELEMENTS(labels),
2299 sp_rtb_ry_value_changed);
2300 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2301 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2302 }
2304 // add the units menu
2305 {
2306 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2307 gtk_action_group_add_action( mainActions, act );
2308 }
2310 /* Reset */
2311 {
2312 InkAction* inky = ink_action_new( "RectResetAction",
2313 _("Not rounded"),
2314 _("Make corners sharp"),
2315 "squared_corner",
2316 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2317 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2318 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2319 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2320 g_object_set_data( holder, "not_rounded", inky );
2321 }
2323 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2324 sp_rtb_sensitivize( holder );
2326 sigc::connection *connection = new sigc::connection(
2327 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2328 );
2329 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2330 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2331 }
2333 //########################
2334 //## 3D Box ##
2335 //########################
2337 // normalize angle so that it lies in the interval [0,360]
2338 static double box3d_normalize_angle (double a) {
2339 double angle = a + ((int) (a/360.0))*360;
2340 if (angle < 0) {
2341 angle += 360.0;
2342 }
2343 return angle;
2344 }
2346 static void
2347 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2348 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2349 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2350 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2351 // are reset).
2352 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2354 if (is_infinite) {
2355 gtk_toggle_action_set_active(tact, TRUE);
2356 gtk_action_set_sensitive(act, TRUE);
2358 double angle = persp3d_get_infinite_angle(persp, axis);
2359 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2360 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2361 }
2362 } else {
2363 gtk_toggle_action_set_active(tact, FALSE);
2364 gtk_action_set_sensitive(act, FALSE);
2365 }
2366 }
2368 static void
2369 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2370 if (!persp_repr) {
2371 g_print ("No perspective given to box3d_resync_toolbar().\n");
2372 return;
2373 }
2375 GtkWidget *tbl = GTK_WIDGET(data);
2376 GtkAdjustment *adj = 0;
2377 GtkAction *act = 0;
2378 GtkToggleAction *tact = 0;
2379 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2380 {
2381 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2382 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2383 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2385 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2386 }
2387 {
2388 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2389 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2390 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2392 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2393 }
2394 {
2395 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2396 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2397 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2399 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2400 }
2401 }
2403 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2404 gchar const */*old_value*/, gchar const */*new_value*/,
2405 bool /*is_interactive*/, gpointer data)
2406 {
2407 GtkWidget *tbl = GTK_WIDGET(data);
2409 // quit if run by the attr_changed listener
2410 // note: it used to work without the differently called freeze_ attributes (here and in
2411 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2412 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2413 return;
2414 }
2416 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2417 // sp_document_maybe_done() when the document is undo insensitive)
2418 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2420 // TODO: Only update the appropriate part of the toolbar
2421 // if (!strcmp(name, "inkscape:vp_z")) {
2422 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2423 // }
2425 Persp3D *persp = persp3d_get_from_repr(repr);
2426 persp3d_update_box_reprs(persp);
2428 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2429 }
2431 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2432 {
2433 NULL, /* child_added */
2434 NULL, /* child_removed */
2435 box3d_persp_tb_event_attr_changed,
2436 NULL, /* content_changed */
2437 NULL /* order_changed */
2438 };
2440 /**
2441 * \param selection Should not be NULL.
2442 */
2443 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2444 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2445 static void
2446 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2447 {
2448 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2449 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2450 // update the perspectives with infinite VPs and leave the other ones untouched).
2452 Inkscape::XML::Node *persp_repr = NULL;
2453 purge_repr_listener(tbl, tbl);
2455 SPItem *item = selection->singleItem();
2456 if (item && SP_IS_BOX3D(item)) {
2457 // FIXME: Also deal with multiple selected boxes
2458 SPBox3D *box = SP_BOX3D(item);
2459 Persp3D *persp = box3d_get_perspective(box);
2460 persp_repr = SP_OBJECT_REPR(persp);
2461 if (persp_repr) {
2462 g_object_set_data(tbl, "repr", persp_repr);
2463 Inkscape::GC::anchor(persp_repr);
2464 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2465 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2466 }
2468 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2469 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2471 box3d_resync_toolbar(persp_repr, tbl);
2472 }
2473 }
2475 static void
2476 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2477 {
2478 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2479 SPDocument *document = sp_desktop_document(desktop);
2481 // quit if run by the attr_changed listener
2482 // note: it used to work without the differently called freeze_ attributes (here and in
2483 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2484 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2485 return;
2486 }
2488 // in turn, prevent listener from responding
2489 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2491 //Persp3D *persp = document->current_persp3d;
2492 std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
2493 if (sel_persps.empty()) {
2494 // this can happen when the document is created; we silently ignore it
2495 return;
2496 }
2497 Persp3D *persp = *(sel_persps.begin());
2499 persp->tmat.set_infinite_direction (axis, adj->value);
2500 SP_OBJECT(persp)->updateRepr();
2502 // TODO: use the correct axis here, too
2503 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2505 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2506 }
2509 static void
2510 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2511 {
2512 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2513 }
2515 static void
2516 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2517 {
2518 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2519 }
2521 static void
2522 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2523 {
2524 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2525 }
2528 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction *box3d_angle, Proj::Axis axis )
2529 {
2530 // TODO: Take all selected perspectives into account
2531 std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
2532 if (sel_persps.empty()) {
2533 // this can happen when the document is created; we silently ignore it
2534 return;
2535 }
2536 Persp3D *persp = *(sel_persps.begin());
2538 bool set_infinite = gtk_toggle_action_get_active(act);
2539 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2540 }
2542 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2543 {
2544 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2545 }
2547 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2548 {
2549 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2550 }
2552 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2553 {
2554 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2555 }
2557 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2558 {
2559 EgeAdjustmentAction* eact = 0;
2560 SPDocument *document = sp_desktop_document (desktop);
2561 Persp3D *persp = document->current_persp3d;
2563 EgeAdjustmentAction* box3d_angle_x = 0;
2564 EgeAdjustmentAction* box3d_angle_y = 0;
2565 EgeAdjustmentAction* box3d_angle_z = 0;
2567 /* Angle X */
2568 {
2569 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2570 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2571 eact = create_adjustment_action( "3DBoxAngleXAction",
2572 _("Angle in X direction"), _("Angle X:"),
2573 // Translators: PL is short for 'perspective line'
2574 _("Angle of PLs in X direction"),
2575 "tools.shapes.3dbox", "box3d_angle_x", 30,
2576 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2577 -360.0, 360.0, 1.0, 10.0,
2578 labels, values, G_N_ELEMENTS(labels),
2579 box3d_angle_x_value_changed );
2580 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2581 g_object_set_data( holder, "box3d_angle_x_action", eact );
2582 box3d_angle_x = eact;
2583 }
2585 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2586 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2587 } else {
2588 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2589 }
2592 /* VP X state */
2593 {
2594 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2595 // Translators: VP is short for 'vanishing point'
2596 _("State of VP in X direction"),
2597 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2598 "toggle_vp_x",
2599 Inkscape::ICON_SIZE_DECORATION );
2600 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2601 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2602 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2603 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2604 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2605 }
2607 /* Angle Y */
2608 {
2609 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2610 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2611 eact = create_adjustment_action( "3DBoxAngleYAction",
2612 _("Angle in Y direction"), _("Angle Y:"),
2613 // Translators: PL is short for 'perspective line'
2614 _("Angle of PLs in Y direction"),
2615 "tools.shapes.3dbox", "box3d_angle_y", 30,
2616 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2617 -360.0, 360.0, 1.0, 10.0,
2618 labels, values, G_N_ELEMENTS(labels),
2619 box3d_angle_y_value_changed );
2620 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2621 g_object_set_data( holder, "box3d_angle_y_action", eact );
2622 box3d_angle_y = eact;
2623 }
2625 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2626 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2627 } else {
2628 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2629 }
2631 /* VP Y state */
2632 {
2633 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2634 // Translators: VP is short for 'vanishing point'
2635 _("State of VP in Y direction"),
2636 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2637 "toggle_vp_y",
2638 Inkscape::ICON_SIZE_DECORATION );
2639 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2640 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2641 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2642 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2643 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2644 }
2646 /* Angle Z */
2647 {
2648 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2649 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2650 eact = create_adjustment_action( "3DBoxAngleZAction",
2651 _("Angle in Z direction"), _("Angle Z:"),
2652 // Translators: PL is short for 'perspective line'
2653 _("Angle of PLs in Z direction"),
2654 "tools.shapes.3dbox", "box3d_angle_z", 30,
2655 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2656 -360.0, 360.0, 1.0, 10.0,
2657 labels, values, G_N_ELEMENTS(labels),
2658 box3d_angle_z_value_changed );
2659 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2660 g_object_set_data( holder, "box3d_angle_z_action", eact );
2661 box3d_angle_z = eact;
2662 }
2664 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2665 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2666 } else {
2667 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2668 }
2670 /* VP Z state */
2671 {
2672 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2673 // Translators: VP is short for 'vanishing point'
2674 _("State of VP in Z direction"),
2675 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2676 "toggle_vp_z",
2677 Inkscape::ICON_SIZE_DECORATION );
2678 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2679 g_object_set_data( holder, "box3d_vp_z_state_action", act );
2680 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2681 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2682 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2683 }
2685 sigc::connection *connection = new sigc::connection(
2686 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2687 );
2688 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2689 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2690 }
2692 //########################
2693 //## Spiral ##
2694 //########################
2696 static void
2697 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2698 {
2699 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2701 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2702 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2703 }
2705 // quit if run by the attr_changed listener
2706 if (g_object_get_data( tbl, "freeze" )) {
2707 return;
2708 }
2710 // in turn, prevent listener from responding
2711 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2713 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2715 bool modmade = false;
2716 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2717 items != NULL;
2718 items = items->next)
2719 {
2720 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2721 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2722 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2723 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2724 modmade = true;
2725 }
2726 }
2728 g_free(namespaced_name);
2730 if (modmade) {
2731 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2732 _("Change spiral"));
2733 }
2735 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2736 }
2738 static void
2739 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2740 {
2741 sp_spl_tb_value_changed(adj, tbl, "revolution");
2742 }
2744 static void
2745 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2746 {
2747 sp_spl_tb_value_changed(adj, tbl, "expansion");
2748 }
2750 static void
2751 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2752 {
2753 sp_spl_tb_value_changed(adj, tbl, "t0");
2754 }
2756 static void
2757 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2758 {
2759 GtkWidget *tbl = GTK_WIDGET(obj);
2761 GtkAdjustment *adj;
2763 // fixme: make settable
2764 gdouble rev = 5;
2765 gdouble exp = 1.0;
2766 gdouble t0 = 0.0;
2768 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2769 gtk_adjustment_set_value(adj, rev);
2770 gtk_adjustment_value_changed(adj);
2772 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2773 gtk_adjustment_set_value(adj, exp);
2774 gtk_adjustment_value_changed(adj);
2776 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2777 gtk_adjustment_set_value(adj, t0);
2778 gtk_adjustment_value_changed(adj);
2780 spinbutton_defocus(GTK_OBJECT(tbl));
2781 }
2784 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2785 gchar const */*old_value*/, gchar const */*new_value*/,
2786 bool /*is_interactive*/, gpointer data)
2787 {
2788 GtkWidget *tbl = GTK_WIDGET(data);
2790 // quit if run by the _changed callbacks
2791 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2792 return;
2793 }
2795 // in turn, prevent callbacks from responding
2796 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2798 GtkAdjustment *adj;
2799 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2800 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2802 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2803 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2805 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2806 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2808 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2809 }
2812 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2813 NULL, /* child_added */
2814 NULL, /* child_removed */
2815 spiral_tb_event_attr_changed,
2816 NULL, /* content_changed */
2817 NULL /* order_changed */
2818 };
2820 static void
2821 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2822 {
2823 int n_selected = 0;
2824 Inkscape::XML::Node *repr = NULL;
2826 purge_repr_listener( tbl, tbl );
2828 for (GSList const *items = selection->itemList();
2829 items != NULL;
2830 items = items->next)
2831 {
2832 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2833 n_selected++;
2834 repr = SP_OBJECT_REPR((SPItem *) items->data);
2835 }
2836 }
2838 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2840 if (n_selected == 0) {
2841 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2842 } else if (n_selected == 1) {
2843 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2845 if (repr) {
2846 g_object_set_data( tbl, "repr", repr );
2847 Inkscape::GC::anchor(repr);
2848 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2849 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2850 }
2851 } else {
2852 // FIXME: implement averaging of all parameters for multiple selected
2853 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2854 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2855 }
2856 }
2859 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2860 {
2861 EgeAdjustmentAction* eact = 0;
2863 {
2864 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2865 ege_output_action_set_use_markup( act, TRUE );
2866 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2867 g_object_set_data( holder, "mode_action", act );
2868 }
2870 /* Revolution */
2871 {
2872 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2873 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2874 eact = create_adjustment_action( "SpiralRevolutionAction",
2875 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
2876 "tools.shapes.spiral", "revolution", 3.0,
2877 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2878 0.01, 1024.0, 0.1, 1.0,
2879 labels, values, G_N_ELEMENTS(labels),
2880 sp_spl_tb_revolution_value_changed, 1, 2);
2881 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2882 }
2884 /* Expansion */
2885 {
2886 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2887 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2888 eact = create_adjustment_action( "SpiralExpansionAction",
2889 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2890 "tools.shapes.spiral", "expansion", 1.0,
2891 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2892 0.0, 1000.0, 0.01, 1.0,
2893 labels, values, G_N_ELEMENTS(labels),
2894 sp_spl_tb_expansion_value_changed);
2895 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2896 }
2898 /* T0 */
2899 {
2900 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2901 gdouble values[] = {0, 0.5, 0.9};
2902 eact = create_adjustment_action( "SpiralT0Action",
2903 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2904 "tools.shapes.spiral", "t0", 0.0,
2905 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2906 0.0, 0.999, 0.01, 1.0,
2907 labels, values, G_N_ELEMENTS(labels),
2908 sp_spl_tb_t0_value_changed);
2909 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2910 }
2912 /* Reset */
2913 {
2914 InkAction* inky = ink_action_new( "SpiralResetAction",
2915 _("Defaults"),
2916 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2917 GTK_STOCK_CLEAR,
2918 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2919 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2920 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2921 }
2924 sigc::connection *connection = new sigc::connection(
2925 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2926 );
2927 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2928 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2929 }
2931 //########################
2932 //## Pen/Pencil ##
2933 //########################
2936 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2937 {
2938 // Put stuff here
2939 }
2941 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2942 {
2943 // Put stuff here
2944 }
2946 //########################
2947 //## Tweak ##
2948 //########################
2950 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2951 {
2952 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2953 }
2955 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2956 {
2957 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2958 }
2960 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
2961 {
2962 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2963 }
2965 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2966 {
2967 int mode = ege_select_one_action_get_active( act );
2968 prefs_set_int_attribute("tools.tweak", "mode", mode);
2970 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2971 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2972 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2973 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2974 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2975 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2976 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
2977 if (doh) gtk_action_set_sensitive (doh, TRUE);
2978 if (dos) gtk_action_set_sensitive (dos, TRUE);
2979 if (dol) gtk_action_set_sensitive (dol, TRUE);
2980 if (doo) gtk_action_set_sensitive (doo, TRUE);
2981 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
2982 if (fid) gtk_action_set_sensitive (fid, FALSE);
2983 } else {
2984 if (doh) gtk_action_set_sensitive (doh, FALSE);
2985 if (dos) gtk_action_set_sensitive (dos, FALSE);
2986 if (dol) gtk_action_set_sensitive (dol, FALSE);
2987 if (doo) gtk_action_set_sensitive (doo, FALSE);
2988 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
2989 if (fid) gtk_action_set_sensitive (fid, TRUE);
2990 }
2991 }
2993 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2994 {
2995 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
2996 }
2998 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
2999 bool show = gtk_toggle_action_get_active( act );
3000 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3001 }
3002 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3003 bool show = gtk_toggle_action_get_active( act );
3004 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3005 }
3006 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3007 bool show = gtk_toggle_action_get_active( act );
3008 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3009 }
3010 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3011 bool show = gtk_toggle_action_get_active( act );
3012 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3013 }
3015 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3016 {
3017 {
3018 /* Width */
3019 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3020 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3021 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3022 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3023 "tools.tweak", "width", 15,
3024 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3025 1, 100, 1.0, 10.0,
3026 labels, values, G_N_ELEMENTS(labels),
3027 sp_tweak_width_value_changed, 0.01, 0, 100 );
3028 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3029 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3030 }
3033 {
3034 /* Force */
3035 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3036 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3037 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3038 _("Force"), _("Force:"), _("The force of the tweak action"),
3039 "tools.tweak", "force", 20,
3040 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3041 1, 100, 1.0, 10.0,
3042 labels, values, G_N_ELEMENTS(labels),
3043 sp_tweak_force_value_changed, 0.01, 0, 100 );
3044 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3045 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3046 }
3048 /* Mode */
3049 {
3050 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3052 GtkTreeIter iter;
3053 gtk_list_store_append( model, &iter );
3054 gtk_list_store_set( model, &iter,
3055 0, _("Push mode"),
3056 1, _("Push parts of paths in any direction"),
3057 2, "tweak_push_mode",
3058 -1 );
3060 gtk_list_store_append( model, &iter );
3061 gtk_list_store_set( model, &iter,
3062 0, _("Shrink mode"),
3063 1, _("Shrink (inset) parts of paths"),
3064 2, "tweak_shrink_mode",
3065 -1 );
3067 gtk_list_store_append( model, &iter );
3068 gtk_list_store_set( model, &iter,
3069 0, _("Grow mode"),
3070 1, _("Grow (outset) parts of paths"),
3071 2, "tweak_grow_mode",
3072 -1 );
3074 gtk_list_store_append( model, &iter );
3075 gtk_list_store_set( model, &iter,
3076 0, _("Attract mode"),
3077 1, _("Attract parts of paths towards cursor"),
3078 2, "tweak_attract_mode",
3079 -1 );
3081 gtk_list_store_append( model, &iter );
3082 gtk_list_store_set( model, &iter,
3083 0, _("Repel mode"),
3084 1, _("Repel parts of paths from cursor"),
3085 2, "tweak_repel_mode",
3086 -1 );
3088 gtk_list_store_append( model, &iter );
3089 gtk_list_store_set( model, &iter,
3090 0, _("Roughen mode"),
3091 1, _("Roughen parts of paths"),
3092 2, "tweak_roughen_mode",
3093 -1 );
3095 gtk_list_store_append( model, &iter );
3096 gtk_list_store_set( model, &iter,
3097 0, _("Color paint mode"),
3098 1, _("Paint the tool's color upon selected objects"),
3099 2, "tweak_colorpaint_mode",
3100 -1 );
3102 gtk_list_store_append( model, &iter );
3103 gtk_list_store_set( model, &iter,
3104 0, _("Color jitter mode"),
3105 1, _("Jitter the colors of selected objects"),
3106 2, "tweak_colorjitter_mode",
3107 -1 );
3109 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3110 g_object_set( act, "short_label", _("Mode:"), NULL );
3111 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3112 g_object_set_data( holder, "mode_action", act );
3114 ege_select_one_action_set_appearance( act, "full" );
3115 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3116 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3117 ege_select_one_action_set_icon_column( act, 2 );
3118 ege_select_one_action_set_tooltip_column( act, 1 );
3120 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3121 ege_select_one_action_set_active( act, mode );
3122 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3124 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3125 }
3127 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3129 {
3130 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3131 ege_output_action_set_use_markup( act, TRUE );
3132 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3133 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3134 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3135 g_object_set_data( holder, "tweak_channels_label", act);
3136 }
3138 {
3139 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3140 _("Hue"),
3141 _("In color mode, act on objects' hue"),
3142 NULL,
3143 Inkscape::ICON_SIZE_DECORATION );
3144 g_object_set( act, "short_label", _("H"), NULL );
3145 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3146 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3147 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3148 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3149 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3150 g_object_set_data( holder, "tweak_doh", act);
3151 }
3152 {
3153 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3154 _("Saturation"),
3155 _("In color mode, act on objects' saturation"),
3156 NULL,
3157 Inkscape::ICON_SIZE_DECORATION );
3158 g_object_set( act, "short_label", _("S"), NULL );
3159 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3160 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3161 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3162 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3163 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3164 g_object_set_data( holder, "tweak_dos", act );
3165 }
3166 {
3167 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3168 _("Lightness"),
3169 _("In color mode, act on objects' lightness"),
3170 NULL,
3171 Inkscape::ICON_SIZE_DECORATION );
3172 g_object_set( act, "short_label", _("L"), NULL );
3173 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3174 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3175 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3176 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3177 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3178 g_object_set_data( holder, "tweak_dol", act );
3179 }
3180 {
3181 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3182 _("Opacity"),
3183 _("In color mode, act on objects' opacity"),
3184 NULL,
3185 Inkscape::ICON_SIZE_DECORATION );
3186 g_object_set( act, "short_label", _("O"), NULL );
3187 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3188 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3189 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3190 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3191 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3192 g_object_set_data( holder, "tweak_doo", act );
3193 }
3195 { /* Fidelity */
3196 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3197 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3198 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3199 _("Fidelity"), _("Fidelity:"),
3200 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3201 "tools.tweak", "fidelity", 50,
3202 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3203 1, 100, 1.0, 10.0,
3204 labels, values, G_N_ELEMENTS(labels),
3205 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3206 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3207 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3208 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3209 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3210 g_object_set_data( holder, "tweak_fidelity", eact );
3211 }
3214 /* Use Pressure button */
3215 {
3216 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3217 _("Pressure"),
3218 _("Use the pressure of the input device to alter the force of tweak action"),
3219 "use_pressure",
3220 Inkscape::ICON_SIZE_DECORATION );
3221 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3222 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3223 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3224 }
3226 }
3229 //########################
3230 //## Calligraphy ##
3231 //########################
3233 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3234 {
3235 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3236 }
3238 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3239 {
3240 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3241 }
3243 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3244 {
3245 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3246 }
3248 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3249 {
3250 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3251 }
3253 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3254 {
3255 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3256 }
3258 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3259 {
3260 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3261 }
3263 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3264 {
3265 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3266 }
3268 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3269 {
3270 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3271 }
3273 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3274 {
3275 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3276 }
3278 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3279 {
3280 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3281 }
3283 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3284 {
3285 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3287 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3288 }
3290 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3291 {
3292 // FIXME: make defaults settable via Inkscape Options
3293 struct KeyValue {
3294 char const *key;
3295 double value;
3296 } const key_values[] = {
3297 {"mass", 0.02},
3298 {"wiggle", 0.0},
3299 {"angle", 30.0},
3300 {"width", 15},
3301 {"thinning", 0.1},
3302 {"tremor", 0.0},
3303 {"flatness", 0.9},
3304 {"cap_rounding", 0.0}
3305 };
3307 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3308 KeyValue const &kv = key_values[i];
3309 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3310 if ( adj ) {
3311 gtk_adjustment_set_value(adj, kv.value);
3312 }
3313 }
3314 }
3317 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3318 {
3319 {
3320 EgeAdjustmentAction* calligraphy_angle = 0;
3322 {
3323 /* Width */
3324 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3325 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3326 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3327 _("Pen Width"), _("Width:"),
3328 _("The width of the calligraphic pen (relative to the visible canvas area)"),
3329 "tools.calligraphic", "width", 15,
3330 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3331 1, 100, 1.0, 10.0,
3332 labels, values, G_N_ELEMENTS(labels),
3333 sp_ddc_width_value_changed, 0.01, 0, 100 );
3334 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3335 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3336 }
3338 {
3339 /* Thinning */
3340 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3341 gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3342 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3343 _("Stroke Thinning"), _("Thinning:"),
3344 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3345 "tools.calligraphic", "thinning", 0.1,
3346 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3347 -1.0, 1.0, 0.01, 0.1,
3348 labels, values, G_N_ELEMENTS(labels),
3349 sp_ddc_velthin_value_changed, 0.01, 2);
3350 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3351 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3352 }
3354 {
3355 /* Angle */
3356 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3357 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3358 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3359 _("Pen Angle"), _("Angle:"),
3360 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3361 "tools.calligraphic", "angle", 30,
3362 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3363 -90.0, 90.0, 1.0, 10.0,
3364 labels, values, G_N_ELEMENTS(labels),
3365 sp_ddc_angle_value_changed, 1, 0 );
3366 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3367 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3368 calligraphy_angle = eact;
3369 }
3371 {
3372 /* Fixation */
3373 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3374 gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3375 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3376 _("Fixation"), _("Fixation:"),
3377 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3378 "tools.calligraphic", "flatness", 0.9,
3379 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3380 0.0, 1.0, 0.01, 0.1,
3381 labels, values, G_N_ELEMENTS(labels),
3382 sp_ddc_flatness_value_changed, 0.01, 2 );
3383 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3384 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3385 }
3387 {
3388 /* Cap Rounding */
3389 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3390 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3391 // TRANSLATORS: "cap" means "end" (both start and finish) here
3392 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3393 _("Cap rounding"), _("Caps:"),
3394 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3395 "tools.calligraphic", "cap_rounding", 0.0,
3396 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3397 0.0, 5.0, 0.01, 0.1,
3398 labels, values, G_N_ELEMENTS(labels),
3399 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3400 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3401 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3402 }
3404 {
3405 /* Tremor */
3406 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3407 gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3408 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3409 _("Stroke Tremor"), _("Tremor:"),
3410 _("Increase to make strokes rugged and trembling"),
3411 "tools.calligraphic", "tremor", 0.0,
3412 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3413 0.0, 1.0, 0.01, 0.1,
3414 labels, values, G_N_ELEMENTS(labels),
3415 sp_ddc_tremor_value_changed, 0.01, 2 );
3417 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3418 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3419 }
3421 {
3422 /* Wiggle */
3423 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3424 gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3425 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3426 _("Pen Wiggle"), _("Wiggle:"),
3427 _("Increase to make the pen waver and wiggle"),
3428 "tools.calligraphic", "wiggle", 0.0,
3429 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3430 0.0, 1.0, 0.01, 0.1,
3431 labels, values, G_N_ELEMENTS(labels),
3432 sp_ddc_wiggle_value_changed, 0.01, 2 );
3433 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3434 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3435 }
3437 {
3438 /* Mass */
3439 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3440 gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3441 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3442 _("Pen Mass"), _("Mass:"),
3443 _("Increase to make the pen drag behind, as if slowed by inertia"),
3444 "tools.calligraphic", "mass", 0.02,
3445 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3446 0.0, 1.0, 0.01, 0.1,
3447 labels, values, G_N_ELEMENTS(labels),
3448 sp_ddc_mass_value_changed, 0.01, 2 );
3449 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3450 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3451 }
3454 /* Trace Background button */
3455 {
3456 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3457 _("Trace Background"),
3458 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3459 "trace_background",
3460 Inkscape::ICON_SIZE_DECORATION );
3461 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3462 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3463 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3464 }
3466 /* Use Pressure button */
3467 {
3468 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3469 _("Pressure"),
3470 _("Use the pressure of the input device to alter the width of the pen"),
3471 "use_pressure",
3472 Inkscape::ICON_SIZE_DECORATION );
3473 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3474 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3475 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3476 }
3478 /* Use Tilt button */
3479 {
3480 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3481 _("Tilt"),
3482 _("Use the tilt of the input device to alter the angle of the pen's nib"),
3483 "use_tilt",
3484 Inkscape::ICON_SIZE_DECORATION );
3485 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3486 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3487 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3488 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3489 }
3491 /* Reset */
3492 {
3493 GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3494 _("Defaults"),
3495 _("Reset all parameters to defaults"),
3496 GTK_STOCK_CLEAR );
3497 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3498 gtk_action_group_add_action( mainActions, act );
3499 gtk_action_set_sensitive( act, TRUE );
3500 }
3501 }
3502 }
3505 //########################
3506 //## Circle / Arc ##
3507 //########################
3509 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3510 {
3511 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3512 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3514 if (v1 == 0 && v2 == 0) {
3515 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3516 gtk_action_set_sensitive( ocb, FALSE );
3517 gtk_action_set_sensitive( make_whole, FALSE );
3518 }
3519 } else {
3520 gtk_action_set_sensitive( ocb, TRUE );
3521 gtk_action_set_sensitive( make_whole, TRUE );
3522 }
3523 }
3525 static void
3526 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3527 {
3528 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3530 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3531 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3532 }
3534 // quit if run by the attr_changed listener
3535 if (g_object_get_data( tbl, "freeze" )) {
3536 return;
3537 }
3539 // in turn, prevent listener from responding
3540 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3542 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3544 bool modmade = false;
3545 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3546 items != NULL;
3547 items = items->next)
3548 {
3549 SPItem *item = SP_ITEM(items->data);
3551 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3553 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3554 SPArc *arc = SP_ARC(item);
3556 if (!strcmp(value_name, "start"))
3557 ge->start = (adj->value * M_PI)/ 180;
3558 else
3559 ge->end = (adj->value * M_PI)/ 180;
3561 sp_genericellipse_normalize(ge);
3562 ((SPObject *)arc)->updateRepr();
3563 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3565 modmade = true;
3566 }
3567 }
3569 g_free(namespaced_name);
3571 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3573 sp_arctb_sensitivize( tbl, adj->value, other->value );
3575 if (modmade) {
3576 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3577 _("Arc: Change start/end"));
3578 }
3580 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3581 }
3584 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
3585 {
3586 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
3587 }
3589 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3590 {
3591 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
3592 }
3594 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3595 {
3596 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3597 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3598 if ( ege_select_one_action_get_active( act ) != 0 ) {
3599 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3600 } else {
3601 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3602 }
3603 }
3605 // quit if run by the attr_changed listener
3606 if (g_object_get_data( tbl, "freeze" )) {
3607 return;
3608 }
3610 // in turn, prevent listener from responding
3611 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3613 bool modmade = false;
3615 if ( ege_select_one_action_get_active(act) != 0 ) {
3616 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3617 items != NULL;
3618 items = items->next)
3619 {
3620 if (SP_IS_ARC((SPItem *) items->data)) {
3621 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3622 repr->setAttribute("sodipodi:open", "true");
3623 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3624 modmade = true;
3625 }
3626 }
3627 } else {
3628 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3629 items != NULL;
3630 items = items->next)
3631 {
3632 if (SP_IS_ARC((SPItem *) items->data)) {
3633 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3634 repr->setAttribute("sodipodi:open", NULL);
3635 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3636 modmade = true;
3637 }
3638 }
3639 }
3641 if (modmade) {
3642 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3643 _("Arc: Change open/closed"));
3644 }
3646 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3647 }
3649 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3650 {
3651 GtkAdjustment *adj;
3652 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3653 gtk_adjustment_set_value(adj, 0.0);
3654 gtk_adjustment_value_changed(adj);
3656 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3657 gtk_adjustment_set_value(adj, 0.0);
3658 gtk_adjustment_value_changed(adj);
3660 spinbutton_defocus( GTK_OBJECT(obj) );
3661 }
3663 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3664 gchar const */*old_value*/, gchar const */*new_value*/,
3665 bool /*is_interactive*/, gpointer data)
3666 {
3667 GObject *tbl = G_OBJECT(data);
3669 // quit if run by the _changed callbacks
3670 if (g_object_get_data( tbl, "freeze" )) {
3671 return;
3672 }
3674 // in turn, prevent callbacks from responding
3675 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3677 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3678 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3680 GtkAdjustment *adj1,*adj2;
3681 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3682 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3683 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3684 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3686 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3688 char const *openstr = NULL;
3689 openstr = repr->attribute("sodipodi:open");
3690 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3692 if (openstr) {
3693 ege_select_one_action_set_active( ocb, 1 );
3694 } else {
3695 ege_select_one_action_set_active( ocb, 0 );
3696 }
3698 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3699 }
3701 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3702 NULL, /* child_added */
3703 NULL, /* child_removed */
3704 arc_tb_event_attr_changed,
3705 NULL, /* content_changed */
3706 NULL /* order_changed */
3707 };
3710 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3711 {
3712 int n_selected = 0;
3713 Inkscape::XML::Node *repr = NULL;
3715 purge_repr_listener( tbl, tbl );
3717 for (GSList const *items = selection->itemList();
3718 items != NULL;
3719 items = items->next)
3720 {
3721 if (SP_IS_ARC((SPItem *) items->data)) {
3722 n_selected++;
3723 repr = SP_OBJECT_REPR((SPItem *) items->data);
3724 }
3725 }
3727 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3729 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3730 if (n_selected == 0) {
3731 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3732 } else if (n_selected == 1) {
3733 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3734 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3736 if (repr) {
3737 g_object_set_data( tbl, "repr", repr );
3738 Inkscape::GC::anchor(repr);
3739 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3740 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3741 }
3742 } else {
3743 // FIXME: implement averaging of all parameters for multiple selected
3744 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3745 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3746 sp_arctb_sensitivize( tbl, 1, 0 );
3747 }
3748 }
3751 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3752 {
3753 EgeAdjustmentAction* eact = 0;
3756 {
3757 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3758 ege_output_action_set_use_markup( act, TRUE );
3759 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3760 g_object_set_data( holder, "mode_action", act );
3761 }
3763 /* Start */
3764 {
3765 eact = create_adjustment_action( "ArcStartAction",
3766 _("Start"), _("Start:"),
3767 _("The angle (in degrees) from the horizontal to the arc's start point"),
3768 "tools.shapes.arc", "start", 0.0,
3769 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3770 -360.0, 360.0, 1.0, 10.0,
3771 0, 0, 0,
3772 sp_arctb_start_value_changed);
3773 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3774 }
3776 /* End */
3777 {
3778 eact = create_adjustment_action( "ArcEndAction",
3779 _("End"), _("End:"),
3780 _("The angle (in degrees) from the horizontal to the arc's end point"),
3781 "tools.shapes.arc", "end", 0.0,
3782 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3783 -360.0, 360.0, 1.0, 10.0,
3784 0, 0, 0,
3785 sp_arctb_end_value_changed);
3786 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3787 }
3789 /* Segments / Pie checkbox */
3790 {
3791 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3793 GtkTreeIter iter;
3794 gtk_list_store_append( model, &iter );
3795 gtk_list_store_set( model, &iter,
3796 0, _("Closed arc"),
3797 1, _("Switch to segment (closed shape with two radii)"),
3798 2, "circle_closed_arc",
3799 -1 );
3801 gtk_list_store_append( model, &iter );
3802 gtk_list_store_set( model, &iter,
3803 0, _("Open Arc"),
3804 1, _("Switch to arc (unclosed shape)"),
3805 2, "circle_open_arc",
3806 -1 );
3808 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3809 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3810 g_object_set_data( holder, "open_action", act );
3812 ege_select_one_action_set_appearance( act, "full" );
3813 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3814 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3815 ege_select_one_action_set_icon_column( act, 2 );
3816 ege_select_one_action_set_tooltip_column( act, 1 );
3818 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3819 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3820 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3821 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3822 }
3824 /* Make Whole */
3825 {
3826 InkAction* inky = ink_action_new( "ArcResetAction",
3827 _("Make whole"),
3828 _("Make the shape a whole ellipse, not arc or segment"),
3829 "reset_circle",
3830 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3831 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3832 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3833 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3834 g_object_set_data( holder, "make_whole", inky );
3835 }
3837 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3838 // sensitivize make whole and open checkbox
3839 {
3840 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3841 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3842 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3843 }
3846 sigc::connection *connection = new sigc::connection(
3847 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3848 );
3849 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3850 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3851 }
3856 // toggle button callbacks and updaters
3858 //########################
3859 //## Dropper ##
3860 //########################
3862 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3863 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3864 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3865 if ( set_action ) {
3866 if ( gtk_toggle_action_get_active( act ) ) {
3867 gtk_action_set_sensitive( set_action, TRUE );
3868 } else {
3869 gtk_action_set_sensitive( set_action, FALSE );
3870 }
3871 }
3873 spinbutton_defocus(GTK_OBJECT(tbl));
3874 }
3876 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3877 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3878 spinbutton_defocus(GTK_OBJECT(tbl));
3879 }
3882 /**
3883 * Dropper auxiliary toolbar construction and setup.
3884 *
3885 * TODO: Would like to add swatch of current color.
3886 * TODO: Add queue of last 5 or so colors selected with new swatches so that
3887 * can drag and drop places. Will provide a nice mixing palette.
3888 */
3889 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3890 {
3891 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3893 {
3894 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3895 _("Pick alpha"),
3896 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3897 "color_alpha_get",
3898 Inkscape::ICON_SIZE_DECORATION );
3899 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3900 g_object_set_data( holder, "pick_action", act );
3901 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3902 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3903 }
3905 {
3906 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3907 _("Set alpha"),
3908 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3909 "color_alpha_set",
3910 Inkscape::ICON_SIZE_DECORATION );
3911 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3912 g_object_set_data( holder, "set_action", act );
3913 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3914 // make sure it's disabled if we're not picking alpha
3915 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3916 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3917 }
3918 }
3921 //########################
3922 //## Text Toolbox ##
3923 //########################
3924 /*
3925 static void
3926 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3927 {
3928 //Call back for letter sizing spinbutton
3929 }
3931 static void
3932 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3933 {
3934 //Call back for line height spinbutton
3935 }
3937 static void
3938 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3939 {
3940 //Call back for horizontal kerning spinbutton
3941 }
3943 static void
3944 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3945 {
3946 //Call back for vertical kerning spinbutton
3947 }
3949 static void
3950 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3951 {
3952 //Call back for letter rotation spinbutton
3953 }*/
3955 namespace {
3957 bool visible = false;
3959 void
3960 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
3961 {
3962 SPStyle *query =
3963 sp_style_new (SP_ACTIVE_DOCUMENT);
3965 int result_fontspec =
3966 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
3968 int result_family =
3969 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3971 int result_style =
3972 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3974 int result_numbers =
3975 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
3977 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
3979 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
3980 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
3981 {
3982 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
3984 if (repr)
3985 {
3986 sp_style_read_from_repr (query, repr);
3987 }
3988 else
3989 {
3990 return;
3991 }
3992 }
3994 if (query->text)
3995 {
3996 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
3997 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
3998 gtk_entry_set_text (GTK_ENTRY (entry), "");
4000 } else if (query->text->font_specification.value || query->text->font_family.value) {
4002 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4004 // Get the font that corresponds
4005 Glib::ustring familyName;
4007 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4008 if (font) {
4009 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4010 font->Unref();
4011 font = NULL;
4012 }
4014 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4016 Gtk::TreePath path;
4017 try {
4018 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4019 } catch (...) {
4020 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4021 return;
4022 }
4024 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4025 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4027 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4029 gtk_tree_selection_select_path (tselection, path.gobj());
4030 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4032 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4033 }
4035 //Size
4036 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4037 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4038 g_object_set_data (tbl, "size-block", gpointer(1));
4039 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4040 g_object_set_data (tbl, "size-block", gpointer(0));
4041 free (str);
4043 //Anchor
4044 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4045 {
4046 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4047 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4048 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4049 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4050 }
4051 else
4052 {
4053 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4054 {
4055 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4056 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4057 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4058 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4059 }
4060 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4061 {
4062 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4063 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4064 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4065 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4066 }
4067 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4068 {
4069 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4070 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4071 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4072 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4073 }
4074 }
4076 //Style
4077 {
4078 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4080 gboolean active = gtk_toggle_button_get_active (button);
4081 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4083 if (active != check)
4084 {
4085 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4086 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4087 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4088 }
4089 }
4091 {
4092 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4094 gboolean active = gtk_toggle_button_get_active (button);
4095 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4097 if (active != check)
4098 {
4099 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4100 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4101 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4102 }
4103 }
4105 //Orientation
4106 //locking both buttons, changing one affect all group (both)
4107 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4108 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4110 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4111 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4113 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4114 {
4115 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4116 }
4117 else
4118 {
4119 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4120 }
4121 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4122 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4123 }
4125 sp_style_unref(query);
4126 }
4128 void
4129 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4130 {
4131 sp_text_toolbox_selection_changed (selection, tbl);
4132 }
4134 void
4135 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4136 {
4137 sp_text_toolbox_selection_changed (NULL, tbl);
4138 }
4140 void
4141 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
4142 GObject *tbl)
4143 {
4144 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4145 GtkTreeModel *model = 0;
4146 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4147 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4148 GtkTreeIter iter;
4149 char *family = 0;
4151 (void)popdown;
4153 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4154 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4156 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4157 return;
4158 }
4160 gtk_tree_model_get (model, &iter, 0, &family, -1);
4162 if (g_object_get_data (G_OBJECT (selection), "block"))
4163 {
4164 gtk_entry_set_text (GTK_ENTRY (entry), family);
4165 return;
4166 }
4168 gtk_entry_set_text (GTK_ENTRY (entry), family);
4170 SPStyle *query =
4171 sp_style_new (SP_ACTIVE_DOCUMENT);
4173 int result_fontspec =
4174 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4176 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4178 SPCSSAttr *css = sp_repr_css_attr_new ();
4181 // First try to get the font spec from the stored value
4182 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4184 if (fontSpec.empty()) {
4185 // Construct a new font specification if it does not yet exist
4186 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4187 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4188 fontFromStyle->Unref();
4189 }
4191 if (!fontSpec.empty()) {
4192 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4193 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4194 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4195 if (font) {
4196 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4198 // Set all the these just in case they were altered when finding the best
4199 // match for the new family and old style...
4201 gchar c[256];
4203 font->Family(c, 256);
4204 sp_repr_css_set_property (css, "font-family", c);
4206 font->Attribute( "weight", c, 256);
4207 sp_repr_css_set_property (css, "font-weight", c);
4209 font->Attribute("style", c, 256);
4210 sp_repr_css_set_property (css, "font-style", c);
4212 font->Attribute("stretch", c, 256);
4213 sp_repr_css_set_property (css, "font-stretch", c);
4215 font->Attribute("variant", c, 256);
4216 sp_repr_css_set_property (css, "font-variant", c);
4218 font->Unref();
4219 }
4220 }
4221 }
4223 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4224 if (result_fontspec == QUERY_STYLE_NOTHING)
4225 {
4226 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4227 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4228 }
4229 else
4230 {
4231 sp_desktop_set_style (desktop, css, true, true);
4232 }
4234 sp_style_unref(query);
4236 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4237 _("Text: Change font family"));
4238 sp_repr_css_attr_unref (css);
4239 free (family);
4240 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4242 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4243 }
4245 void
4246 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
4247 GObject *tbl)
4248 {
4249 const char *family = gtk_entry_get_text (entry);
4251 try {
4252 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4253 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4254 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4255 gtk_tree_selection_select_path (selection, path.gobj());
4256 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4257 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4258 } catch (...) {
4259 if (family && strlen (family))
4260 {
4261 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4262 }
4263 }
4264 }
4266 void
4267 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
4268 gpointer data)
4269 {
4270 if (g_object_get_data (G_OBJECT (button), "block")) return;
4271 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4272 int prop = GPOINTER_TO_INT(data);
4274 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4275 SPCSSAttr *css = sp_repr_css_attr_new ();
4277 switch (prop)
4278 {
4279 case 0:
4280 {
4281 sp_repr_css_set_property (css, "text-anchor", "start");
4282 sp_repr_css_set_property (css, "text-align", "start");
4283 break;
4284 }
4285 case 1:
4286 {
4287 sp_repr_css_set_property (css, "text-anchor", "middle");
4288 sp_repr_css_set_property (css, "text-align", "center");
4289 break;
4290 }
4292 case 2:
4293 {
4294 sp_repr_css_set_property (css, "text-anchor", "end");
4295 sp_repr_css_set_property (css, "text-align", "end");
4296 break;
4297 }
4299 case 3:
4300 {
4301 sp_repr_css_set_property (css, "text-anchor", "start");
4302 sp_repr_css_set_property (css, "text-align", "justify");
4303 break;
4304 }
4305 }
4307 SPStyle *query =
4308 sp_style_new (SP_ACTIVE_DOCUMENT);
4309 int result_numbers =
4310 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4312 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4313 if (result_numbers == QUERY_STYLE_NOTHING)
4314 {
4315 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4316 }
4318 sp_style_unref(query);
4320 sp_desktop_set_style (desktop, css, true, true);
4321 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4322 _("Text: Change alignment"));
4323 sp_repr_css_attr_unref (css);
4325 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4326 }
4328 void
4329 sp_text_toolbox_style_toggled (GtkToggleButton *button,
4330 gpointer data)
4331 {
4332 if (g_object_get_data (G_OBJECT (button), "block")) return;
4334 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4335 SPCSSAttr *css = sp_repr_css_attr_new ();
4336 int prop = GPOINTER_TO_INT(data);
4337 bool active = gtk_toggle_button_get_active (button);
4339 SPStyle *query =
4340 sp_style_new (SP_ACTIVE_DOCUMENT);
4342 int result_fontspec =
4343 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4345 int result_family =
4346 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4348 int result_style =
4349 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4351 int result_numbers =
4352 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4354 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4355 Glib::ustring newFontSpec = "";
4357 if (fontSpec.empty()) {
4358 // Construct a new font specification if it does not yet exist
4359 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4360 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4361 fontFromStyle->Unref();
4362 }
4364 switch (prop)
4365 {
4366 case 0:
4367 {
4368 if (!fontSpec.empty()) {
4369 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4370 }
4371 if (fontSpec != newFontSpec) {
4372 // Don't even set the bold if the font didn't exist on the system
4373 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4374 }
4375 break;
4376 }
4378 case 1:
4379 {
4380 if (!fontSpec.empty()) {
4381 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4382 }
4383 if (fontSpec != newFontSpec) {
4384 // Don't even set the italic if the font didn't exist on the system
4385 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4386 }
4387 break;
4388 }
4389 }
4391 if (!newFontSpec.empty()) {
4392 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4393 }
4395 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4396 if (result_fontspec == QUERY_STYLE_NOTHING)
4397 {
4398 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4399 }
4401 sp_style_unref(query);
4403 sp_desktop_set_style (desktop, css, true, true);
4404 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4405 _("Text: Change font style"));
4406 sp_repr_css_attr_unref (css);
4408 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4409 }
4411 void
4412 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
4413 gpointer data)
4414 {
4415 if (g_object_get_data (G_OBJECT (button), "block")) {
4416 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4417 return;
4418 }
4420 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4421 SPCSSAttr *css = sp_repr_css_attr_new ();
4422 int prop = GPOINTER_TO_INT(data);
4424 switch (prop)
4425 {
4426 case 0:
4427 {
4428 sp_repr_css_set_property (css, "writing-mode", "lr");
4429 break;
4430 }
4432 case 1:
4433 {
4434 sp_repr_css_set_property (css, "writing-mode", "tb");
4435 break;
4436 }
4437 }
4439 SPStyle *query =
4440 sp_style_new (SP_ACTIVE_DOCUMENT);
4441 int result_numbers =
4442 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4444 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4445 if (result_numbers == QUERY_STYLE_NOTHING)
4446 {
4447 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4448 }
4450 sp_desktop_set_style (desktop, css, true, true);
4451 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4452 _("Text: Change orientation"));
4453 sp_repr_css_attr_unref (css);
4455 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4456 }
4458 gboolean
4459 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4460 {
4461 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4462 if (!desktop) return FALSE;
4464 switch (get_group0_keyval (event)) {
4465 case GDK_Escape: // defocus
4466 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4467 sp_text_toolbox_selection_changed (NULL, tbl); // update
4468 return TRUE; // I consumed the event
4469 break;
4470 }
4471 return FALSE;
4472 }
4474 gboolean
4475 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4476 {
4477 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4478 if (!desktop) return FALSE;
4480 switch (get_group0_keyval (event)) {
4481 case GDK_KP_Enter:
4482 case GDK_Return:
4483 case GDK_Escape: // defocus
4484 gtk_widget_hide (w);
4485 visible = false;
4486 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4487 return TRUE; // I consumed the event
4488 break;
4489 }
4490 return FALSE;
4491 }
4494 void
4495 sp_text_toolbox_size_changed (GtkComboBox *cbox,
4496 GObject *tbl)
4497 {
4498 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4500 if (g_object_get_data (tbl, "size-block")) return;
4502 // If this is not from selecting a size in the list (in which case get_active will give the
4503 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4504 // process this event. This fixes GTK's stupid insistence on sending an activate change every
4505 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4506 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4507 return;
4509 gchar *endptr;
4510 gdouble value = -1;
4511 char *text = gtk_combo_box_get_active_text (cbox);
4512 if (text) {
4513 value = g_strtod (text, &endptr);
4514 if (endptr == text) // conversion failed, non-numeric input
4515 value = -1;
4516 free (text);
4517 }
4518 if (value <= 0) {
4519 return; // could not parse value
4520 }
4522 SPCSSAttr *css = sp_repr_css_attr_new ();
4523 Inkscape::CSSOStringStream osfs;
4524 osfs << value;
4525 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4527 SPStyle *query =
4528 sp_style_new (SP_ACTIVE_DOCUMENT);
4529 int result_numbers =
4530 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4532 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4533 if (result_numbers == QUERY_STYLE_NOTHING)
4534 {
4535 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4536 }
4538 sp_style_unref(query);
4540 sp_desktop_set_style (desktop, css, true, true);
4541 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4542 _("Text: Change font size"));
4543 sp_repr_css_attr_unref (css);
4545 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4546 }
4548 gboolean
4549 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus *event, GObject *tbl)
4550 {
4551 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4552 if (!desktop) return FALSE;
4554 if (!g_object_get_data (tbl, "esc-pressed")) {
4555 g_object_set_data (tbl, "enter-pressed", gpointer(1));
4556 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4557 sp_text_toolbox_size_changed (cbox, tbl);
4558 g_object_set_data (tbl, "enter-pressed", gpointer(0));
4559 }
4560 return FALSE; // I consumed the event
4561 }
4564 gboolean
4565 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4566 {
4567 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4568 if (!desktop) return FALSE;
4570 switch (get_group0_keyval (event)) {
4571 case GDK_Escape: // defocus
4572 g_object_set_data (tbl, "esc-pressed", gpointer(1));
4573 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4574 g_object_set_data (tbl, "esc-pressed", gpointer(0));
4575 return TRUE; // I consumed the event
4576 break;
4577 case GDK_Return: // defocus
4578 case GDK_KP_Enter:
4579 g_object_set_data (tbl, "enter-pressed", gpointer(1));
4580 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4581 sp_text_toolbox_size_changed (cbox, tbl);
4582 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4583 g_object_set_data (tbl, "enter-pressed", gpointer(0));
4584 return TRUE; // I consumed the event
4585 break;
4586 }
4587 return FALSE;
4588 }
4590 void
4591 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
4592 GObject *tbl)
4593 {
4594 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4595 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4596 int x, y;
4598 if (!visible)
4599 {
4600 gdk_window_get_origin (widget->window, &x, &y);
4601 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4602 gtk_widget_show_all (popdown);
4604 gdk_pointer_grab (widget->window, TRUE,
4605 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4606 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4607 GDK_POINTER_MOTION_MASK),
4608 NULL, NULL, GDK_CURRENT_TIME);
4610 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4612 visible = true;
4613 }
4614 else
4615 {
4616 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4617 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4618 gtk_widget_hide (popdown);
4619 visible = false;
4620 }
4621 }
4623 gboolean
4624 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
4625 GdkEventFocus */*event*/,
4626 GObject */*tbl*/)
4627 {
4628 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4629 return FALSE;
4630 }
4632 gboolean
4633 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
4634 GdkEventFocus */*event*/,
4635 GObject */*tbl*/)
4636 {
4637 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4639 gtk_widget_hide (popdown);
4640 visible = false;
4641 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4642 return TRUE;
4643 }
4645 void
4646 cell_data_func (GtkTreeViewColumn */*column*/,
4647 GtkCellRenderer *cell,
4648 GtkTreeModel *tree_model,
4649 GtkTreeIter *iter,
4650 gpointer /*data*/)
4651 {
4652 char *family,
4653 *family_escaped,
4654 *sample_escaped;
4656 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4658 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4660 family_escaped = g_markup_escape_text (family, -1);
4661 sample_escaped = g_markup_escape_text (sample, -1);
4663 std::stringstream markup;
4664 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4665 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4667 free (family);
4668 free (family_escaped);
4669 free (sample_escaped);
4670 }
4672 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4673 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4674 if (completion) {
4675 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4676 g_object_unref (completion);
4677 }
4678 }
4680 GtkWidget*
4681 sp_text_toolbox_new (SPDesktop *desktop)
4682 {
4683 GtkWidget *tbl = gtk_hbox_new (FALSE, 0);
4685 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4686 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4688 GtkTooltips *tt = gtk_tooltips_new();
4689 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4691 ////////////Family
4692 //Window
4693 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4694 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4696 //Entry
4697 GtkWidget *entry = gtk_entry_new ();
4698 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4699 GtkEntryCompletion *completion = gtk_entry_completion_new ();
4700 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4701 gtk_entry_completion_set_text_column (completion, 0);
4702 gtk_entry_completion_set_minimum_key_length (completion, 1);
4703 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4704 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4705 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4706 aux_toolbox_space (tbl, 1);
4707 gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4708 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4710 //Button
4711 GtkWidget *button = gtk_button_new ();
4712 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4713 gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4715 //Popdown
4716 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
4717 GtkWidget *treeview = gtk_tree_view_new ();
4719 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
4720 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
4721 gtk_tree_view_column_pack_start (column, cell, FALSE);
4722 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4723 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4724 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4726 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4727 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4728 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4730 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4732 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4733 gtk_container_add (GTK_CONTAINER (sw), treeview);
4735 gtk_container_add (GTK_CONTAINER (window), sw);
4736 gtk_widget_set_size_request (window, 300, 450);
4738 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4739 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4740 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4742 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4744 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4745 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4747 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4748 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4750 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4751 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4752 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4753 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4754 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4756 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4757 aux_toolbox_space (tbl, 1);
4758 GtkWidget *box = gtk_event_box_new ();
4759 gtk_container_add (GTK_CONTAINER (box), image);
4760 gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4761 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4762 GtkTooltips *tooltips = gtk_tooltips_new ();
4763 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4764 gtk_widget_hide (GTK_WIDGET (box));
4765 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4767 ////////////Size
4768 const char *sizes[] = {
4769 "4", "6", "8", "9", "10", "11", "12", "13", "14",
4770 "16", "18", "20", "22", "24", "28",
4771 "32", "36", "40", "48", "56", "64", "72", "144"
4772 };
4774 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4775 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4776 gtk_widget_set_size_request (cbox, 80, -1);
4777 aux_toolbox_space (tbl, 1);
4778 gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4779 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4780 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4781 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
4782 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
4784 //spacer
4785 aux_toolbox_space (tbl, 4);
4786 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4788 ////////////Text anchor
4789 GtkWidget *group = gtk_radio_button_new (NULL);
4790 GtkWidget *row = gtk_hbox_new (FALSE, 4);
4791 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4793 // left
4794 GtkWidget *rbutton = group;
4795 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4796 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4797 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4799 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4800 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
4801 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4802 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4804 // center
4805 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4806 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4807 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4808 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4810 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4811 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
4812 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4813 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4815 // right
4816 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4817 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4818 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4819 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4821 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4822 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
4823 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4824 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4826 // fill
4827 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4828 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4829 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4830 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4832 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4833 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
4834 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4835 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4837 aux_toolbox_space (tbl, 1);
4838 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4840 //spacer
4841 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4843 ////////////Text style
4844 row = gtk_hbox_new (FALSE, 4);
4846 // bold
4847 rbutton = gtk_toggle_button_new ();
4848 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4849 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4850 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4851 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4853 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4854 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
4855 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4857 // italic
4858 rbutton = gtk_toggle_button_new ();
4859 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4860 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4861 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4862 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4864 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4865 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
4866 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4868 aux_toolbox_space (tbl, 1);
4869 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4871 //spacer
4872 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4874 ////////////Text orientation
4875 group = gtk_radio_button_new (NULL);
4876 row = gtk_hbox_new (FALSE, 4);
4877 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4879 // horizontal
4880 rbutton = group;
4881 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4882 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4883 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4884 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4886 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4887 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4888 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4890 // vertical
4891 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4892 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4893 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4894 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4895 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4897 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4898 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
4899 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4900 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4903 //watch selection
4904 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4906 sigc::connection *c_selection_changed =
4907 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4908 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4909 pool->add_connection ("selection-changed", c_selection_changed);
4911 sigc::connection *c_selection_modified =
4912 new sigc::connection (sp_desktop_selection (desktop)->connectModified
4913 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4914 pool->add_connection ("selection-modified", c_selection_modified);
4916 sigc::connection *c_subselection_changed =
4917 new sigc::connection (desktop->connectToolSubselectionChanged
4918 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4919 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4921 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4924 gtk_widget_show_all (tbl);
4925 return tbl;
4927 } // end of sp_text_toolbox_new()
4929 }//<unnamed> namespace
4932 //#########################
4933 //## Connector ##
4934 //#########################
4936 static void sp_connector_path_set_avoid(void)
4937 {
4938 cc_selection_set_avoid(true);
4939 }
4942 static void sp_connector_path_set_ignore(void)
4943 {
4944 cc_selection_set_avoid(false);
4945 }
4949 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4950 {
4951 // quit if run by the _changed callbacks
4952 if (g_object_get_data( tbl, "freeze" )) {
4953 return;
4954 }
4956 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4957 SPDocument *doc = sp_desktop_document(desktop);
4959 if (!sp_document_get_undo_sensitive(doc))
4960 {
4961 return;
4962 }
4964 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
4966 if ( repr->attribute("inkscape:connector-spacing") ) {
4967 gdouble priorValue = gtk_adjustment_get_value(adj);
4968 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
4969 if ( priorValue == gtk_adjustment_get_value(adj) ) {
4970 return;
4971 }
4972 } else if ( adj->value == defaultConnSpacing ) {
4973 return;
4974 }
4976 // in turn, prevent callbacks from responding
4977 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4979 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
4980 SP_OBJECT(desktop->namedview)->updateRepr();
4982 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
4983 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
4984 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
4985 NR::Matrix m = NR::identity();
4986 avoid_item_move(&m, item);
4987 }
4989 if (items) {
4990 g_slist_free(items);
4991 }
4993 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
4994 _("Change connector spacing"));
4996 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4998 spinbutton_defocus(GTK_OBJECT(tbl));
4999 }
5001 static void sp_connector_graph_layout(void)
5002 {
5003 if (!SP_ACTIVE_DESKTOP) return;
5005 // hack for clones, see comment in align-and-distribute.cpp
5006 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5007 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5009 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5011 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5013 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5014 }
5016 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5017 {
5018 if ( gtk_toggle_action_get_active( act ) ) {
5019 prefs_set_string_attribute("tools.connector", "directedlayout",
5020 "true");
5021 } else {
5022 prefs_set_string_attribute("tools.connector", "directedlayout",
5023 "false");
5024 }
5025 }
5027 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5028 {
5029 if ( gtk_toggle_action_get_active( act ) ) {
5030 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5031 "true");
5032 } else {
5033 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5034 "false");
5035 }
5036 }
5039 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5040 {
5041 prefs_set_double_attribute("tools.connector", "length", adj->value);
5042 spinbutton_defocus(GTK_OBJECT(tbl));
5043 }
5045 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5046 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5047 bool /*is_interactive*/, gpointer data)
5048 {
5049 GtkWidget *tbl = GTK_WIDGET(data);
5051 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5052 return;
5053 }
5054 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5055 return;
5056 }
5058 GtkAdjustment *adj = (GtkAdjustment*)
5059 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5060 gdouble spacing = defaultConnSpacing;
5061 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5063 gtk_adjustment_set_value(adj, spacing);
5064 }
5067 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5068 NULL, /* child_added */
5069 NULL, /* child_removed */
5070 connector_tb_event_attr_changed,
5071 NULL, /* content_changed */
5072 NULL /* order_changed */
5073 };
5076 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5077 {
5078 {
5079 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5080 _("Avoid"),
5081 _("Make connectors avoid selected objects"),
5082 "connector_avoid",
5083 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5084 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5085 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5086 }
5088 {
5089 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5090 _("Ignore"),
5091 _("Make connectors ignore selected objects"),
5092 "connector_ignore",
5093 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5094 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5095 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5096 }
5098 EgeAdjustmentAction* eact = 0;
5100 // Spacing spinbox
5101 eact = create_adjustment_action( "ConnectorSpacingAction",
5102 _("Connector Spacing"), _("Spacing:"),
5103 _("The amount of space left around objects by auto-routing connectors"),
5104 "tools.connector", "spacing", defaultConnSpacing,
5105 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5106 0, 100, 1.0, 10.0,
5107 0, 0, 0,
5108 connector_spacing_changed, 1, 0 );
5109 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5111 // Graph (connector network) layout
5112 {
5113 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5114 _("Graph"),
5115 _("Nicely arrange selected connector network"),
5116 "graph_layout",
5117 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5118 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5119 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5120 }
5122 // Default connector length spinbox
5123 eact = create_adjustment_action( "ConnectorLengthAction",
5124 _("Connector Length"), _("Length:"),
5125 _("Ideal length for connectors when layout is applied"),
5126 "tools.connector", "length", 100,
5127 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5128 10, 1000, 10.0, 100.0,
5129 0, 0, 0,
5130 connector_length_changed, 1, 0 );
5131 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5134 // Directed edges toggle button
5135 {
5136 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5137 _("Downwards"),
5138 _("Make connectors with end-markers (arrows) point downwards"),
5139 "directed_graph",
5140 Inkscape::ICON_SIZE_DECORATION );
5141 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5143 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5144 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5145 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5147 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5148 }
5150 // Avoid overlaps toggle button
5151 {
5152 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5153 _("Remove overlaps"),
5154 _("Do not allow overlapping shapes"),
5155 "remove_overlaps",
5156 Inkscape::ICON_SIZE_DECORATION );
5157 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5159 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5160 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5161 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5163 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5164 }
5166 // Code to watch for changes to the connector-spacing attribute in
5167 // the XML.
5168 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5169 g_assert(repr != NULL);
5171 purge_repr_listener( holder, holder );
5173 if (repr) {
5174 g_object_set_data( holder, "repr", repr );
5175 Inkscape::GC::anchor(repr);
5176 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5177 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5178 }
5179 } // end of sp_connector_toolbox_prep()
5182 //#########################
5183 //## Paintbucket ##
5184 //#########################
5186 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5187 {
5188 gint channels = ege_select_one_action_get_active( act );
5189 flood_channels_set_channels( channels );
5190 }
5192 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5193 {
5194 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5195 }
5197 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5198 {
5199 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5200 }
5202 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5203 {
5204 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5205 SPUnit const *unit = tracker->getActiveUnit();
5207 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5209 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5210 }
5212 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5213 {
5214 // FIXME: make defaults settable via Inkscape Options
5215 struct KeyValue {
5216 char const *key;
5217 double value;
5218 } const key_values[] = {
5219 {"threshold", 15},
5220 {"offset", 0.0}
5221 };
5223 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5224 KeyValue const &kv = key_values[i];
5225 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5226 if ( adj ) {
5227 gtk_adjustment_set_value(adj, kv.value);
5228 }
5229 }
5231 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5232 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5233 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5234 ege_select_one_action_set_active( autogap_action, 0 );
5235 }
5237 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5238 {
5239 EgeAdjustmentAction* eact = 0;
5241 {
5242 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5244 GList* items = 0;
5245 gint count = 0;
5246 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5247 {
5248 GtkTreeIter iter;
5249 gtk_list_store_append( model, &iter );
5250 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5251 count++;
5252 }
5253 g_list_free( items );
5254 items = 0;
5255 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5256 g_object_set( act1, "short_label", _("Fill by:"), NULL );
5257 ege_select_one_action_set_appearance( act1, "compact" );
5258 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5259 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5260 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5261 g_object_set_data( holder, "channels_action", act1 );
5262 }
5264 // Spacing spinbox
5265 {
5266 eact = create_adjustment_action(
5267 "ThresholdAction",
5268 _("Fill Threshold"), _("Threshold:"),
5269 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5270 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5271 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5272 0, 0, 0,
5273 paintbucket_threshold_changed, 1, 0 );
5275 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5276 }
5278 // Create the units menu.
5279 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5280 tracker->setActiveUnit(sp_unit_get_by_abbreviation(prefs_get_string_attribute("tools.paintbucket", "offsetunits")));
5281 g_object_set_data( holder, "tracker", tracker );
5282 {
5283 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5284 gtk_action_group_add_action( mainActions, act );
5285 }
5287 // Offset spinbox
5288 {
5289 eact = create_adjustment_action(
5290 "OffsetAction",
5291 _("Grow/shrink by"), _("Grow/shrink by:"),
5292 _("The amount to grow (positive) or shrink (negative) the created fill path"),
5293 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5294 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5295 0, 0, 0,
5296 paintbucket_offset_changed, 1, 2);
5297 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5299 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5300 }
5302 /* Auto Gap */
5303 {
5304 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5306 GList* items = 0;
5307 gint count = 0;
5308 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5309 {
5310 GtkTreeIter iter;
5311 gtk_list_store_append( model, &iter );
5312 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5313 count++;
5314 }
5315 g_list_free( items );
5316 items = 0;
5317 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5318 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5319 ege_select_one_action_set_appearance( act2, "compact" );
5320 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5321 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5322 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5323 g_object_set_data( holder, "autogap_action", act2 );
5324 }
5326 /* Reset */
5327 {
5328 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5329 _("Defaults"),
5330 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5331 GTK_STOCK_CLEAR );
5332 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5333 gtk_action_group_add_action( mainActions, act );
5334 gtk_action_set_sensitive( act, TRUE );
5335 }
5337 }
5339 /*
5340 Local Variables:
5341 mode:c++
5342 c-file-style:"stroustrup"
5343 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5344 indent-tabs-mode:nil
5345 fill-column:99
5346 End:
5347 */
5348 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :