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