df13bed37361adcf4ad94adb223ac9494ff82da2
1 #define __SP_DESKTOP_C__
3 /** \file
4 * Editable view implementation
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * MenTaLguY <mental@rydia.net>
9 * bulia byak <buliabyak@users.sf.net>
10 * Ralf Stephan <ralf@ark.in-berlin.de>
11 * John Bintz <jcoswell@coswellproductions.org>
12 *
13 * Copyright (C) 2006 Johan Engelen <johan@shouraizou.nl>
14 * Copyright (C) 2006 John Bintz
15 * Copyright (C) 2004 MenTaLguY
16 * Copyright (C) 1999-2002 Lauris Kaplinski
17 * Copyright (C) 2000-2001 Ximian, Inc.
18 *
19 * Released under GNU GPL, read the file 'COPYING' for more information
20 */
22 /** \class SPDesktop
23 * SPDesktop is a subclass of View, implementing an editable document
24 * canvas. It is extensively used by many UI controls that need certain
25 * visual representations of their own.
26 *
27 * SPDesktop provides a certain set of SPCanvasItems, serving as GUI
28 * layers of different control objects. The one containing the whole
29 * document is the drawing layer. In addition to it, there are grid,
30 * guide, sketch and control layers. The sketch layer is used for
31 * temporary drawing objects, before the real objects in document are
32 * created. The control layer contains editing knots, rubberband and
33 * similar non-document UI objects.
34 *
35 * Each SPDesktop is associated with a SPNamedView node of the document
36 * tree. Currently, all desktops are created from a single main named
37 * view, but in the future there may be support for different ones.
38 * SPNamedView serves as an in-document container for desktop-related
39 * data, like grid and guideline placement, snapping options and so on.
40 *
41 * Associated with each SPDesktop are the two most important editing
42 * related objects - SPSelection and SPEventContext.
43 *
44 * Sodipodi keeps track of the active desktop and invokes notification
45 * signals whenever it changes. UI elements can use these to update their
46 * display to the selection of the currently active editing window.
47 * (Lauris Kaplinski)
48 */
50 #ifdef HAVE_CONFIG_H
51 # include "config.h"
52 #endif
54 #include <glibmm/i18n.h>
55 #include <sigc++/functors/mem_fun.h>
57 #include "macros.h"
58 #include "inkscape-private.h"
59 #include "desktop.h"
60 #include "desktop-events.h"
61 #include "desktop-handles.h"
62 #include "document.h"
63 #include "message-stack.h"
64 #include "selection.h"
65 #include "select-context.h"
66 #include "sp-namedview.h"
67 #include "color.h"
68 #include "sp-item-group.h"
69 #include "prefs-utils.h"
70 #include "object-hierarchy.h"
71 #include "helper/units.h"
72 #include "display/canvas-arena.h"
73 #include "display/nr-arena.h"
74 #include "display/gnome-canvas-acetate.h"
75 #include "display/sodipodi-ctrlrect.h"
76 #include "display/sp-canvas-util.h"
77 #include "libnr/nr-matrix-div.h"
78 #include "libnr/nr-rect-ops.h"
79 #include "ui/dialog/dialog-manager.h"
80 #include "xml/repr.h"
81 #include "message-context.h"
82 #include "layer-manager.h"
83 #include "event-log.h"
85 namespace Inkscape { namespace XML { class Node; }}
87 // Callback declarations
88 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
89 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
90 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
91 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
92 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
93 static void _reconstruction_start(SPDesktop * desktop);
94 static void _reconstruction_finish(SPDesktop * desktop);
95 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
96 static void _update_snap_distances (SPDesktop *desktop);
98 /**
99 * Return new desktop object.
100 * \pre namedview != NULL.
101 * \pre canvas != NULL.
102 */
103 SPDesktop::SPDesktop()
104 {
105 _dlg_mgr = NULL;
106 _widget = 0;
107 namedview = NULL;
108 selection = NULL;
109 acetate = NULL;
110 main = NULL;
111 grid = NULL;
112 guides = NULL;
113 drawing = NULL;
114 sketch = NULL;
115 controls = NULL;
116 event_context = 0;
117 layer_manager = 0;
119 _d2w.set_identity();
120 _w2d.set_identity();
121 _doc2dt = NR::Matrix(NR::scale(1, -1));
123 guides_active = false;
125 zooms_past = NULL;
126 zooms_future = NULL;
128 is_fullscreen = false;
130 gr_item = NULL;
131 gr_point_num = 0;
132 gr_fill_or_stroke = true;
134 _layer_hierarchy = NULL;
135 _active = false;
137 selection = Inkscape::GC::release (new Inkscape::Selection (this));
138 }
140 void
141 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
143 {
144 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
146 current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
148 namedview = nv;
149 canvas = aCanvas;
151 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
152 /* Kill flicker */
153 sp_document_ensure_up_to_date (document);
155 /* Setup Dialog Manager */
156 _dlg_mgr = new Inkscape::UI::Dialog::DialogManager();
158 dkey = sp_item_display_key_new (1);
160 /* Connect document */
161 setDocument (document);
163 number = namedview->getViewCount();
166 /* Setup Canvas */
167 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
169 SPCanvasGroup *root = sp_canvas_root (canvas);
171 /* Setup adminstrative layers */
172 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
173 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
174 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
175 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
177 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
178 SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
179 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
180 sp_canvas_item_move_to_z (table, 0);
182 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
183 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
184 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
186 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
187 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
189 SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
191 if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
192 // Start in outline mode
193 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
194 canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
195 } else {
196 // Start in normal mode, default
197 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
198 canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
199 }
201 grid = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
202 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
203 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
204 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
206 /* Push select tool to the bottom of stack */
207 /** \todo
208 * FIXME: this is the only call to this. Everything else seems to just
209 * call "set" instead of "push". Can we assume that there is only one
210 * context ever?
211 */
212 push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
214 // display rect and zoom are now handled in sp_desktop_widget_realize()
216 NR::Rect const d(NR::Point(0.0, 0.0),
217 NR::Point(sp_document_width(document), sp_document_height(document)));
219 SP_CTRLRECT(page)->setRectangle(d);
220 SP_CTRLRECT(page_border)->setRectangle(d);
222 /* the following sets the page shadow on the canvas
223 It was originally set to 5, which is really cheesy!
224 It now is an attribute in the document's namedview. If a value of
225 0 is used, then the constructor for a shadow is not initialized.
226 */
228 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
229 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
230 }
233 /* Connect event for page resize */
234 _doc2dt[5] = sp_document_height (document);
235 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
237 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
239 NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
240 SP_CANVAS_ARENA (drawing)->arena,
241 dkey,
242 SP_ITEM_SHOW_DISPLAY);
243 if (ai) {
244 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
245 nr_arena_item_unref (ai);
246 }
248 namedview->show(this);
249 /* Ugly hack */
250 activate_guides (true);
251 /* Ugly hack */
252 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
254 /* Set up notification of rebuilding the document, this allows
255 for saving object related settings in the document. */
256 _reconstruction_start_connection =
257 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
258 _reconstruction_finish_connection =
259 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
260 _reconstruction_old_layer_id = NULL;
262 _commit_connection = document->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
264 // ?
265 // sp_active_desktop_set (desktop);
266 _inkscape = INKSCAPE;
268 _activate_connection = _activate_signal.connect(
269 sigc::bind(
270 sigc::ptr_fun(_onActivate),
271 this
272 )
273 );
274 _deactivate_connection = _deactivate_signal.connect(
275 sigc::bind(
276 sigc::ptr_fun(_onDeactivate),
277 this
278 )
279 );
281 _sel_modified_connection = selection->connectModified(
282 sigc::bind(
283 sigc::ptr_fun(&_onSelectionModified),
284 this
285 )
286 );
287 _sel_changed_connection = selection->connectChanged(
288 sigc::bind(
289 sigc::ptr_fun(&_onSelectionChanged),
290 this
291 )
292 );
295 /* setup LayerManager */
296 // (Setting up after the connections are all in place, as it may use some of them)
297 layer_manager = new Inkscape::LayerManager( this );
298 }
301 void SPDesktop::destroy()
302 {
303 _activate_connection.disconnect();
304 _deactivate_connection.disconnect();
305 _sel_modified_connection.disconnect();
306 _sel_changed_connection.disconnect();
307 _modified_connection.disconnect();
308 _commit_connection.disconnect();
309 _reconstruction_start_connection.disconnect();
310 _reconstruction_finish_connection.disconnect();
312 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
313 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
314 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
316 while (event_context) {
317 SPEventContext *ec = event_context;
318 event_context = ec->next;
319 sp_event_context_finish (ec);
320 g_object_unref (G_OBJECT (ec));
321 }
323 if (_layer_hierarchy) {
324 delete _layer_hierarchy;
325 }
327 if (_inkscape) {
328 _inkscape = NULL;
329 }
331 if (drawing) {
332 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
333 drawing = NULL;
334 }
336 delete _guides_message_context;
337 _guides_message_context = NULL;
339 g_list_free (zooms_past);
340 g_list_free (zooms_future);
341 }
343 SPDesktop::~SPDesktop() {}
345 //--------------------------------------------------------------------
346 /* Public methods */
348 void SPDesktop::setDisplayModeNormal()
349 {
350 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
351 canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
352 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
353 }
355 void SPDesktop::setDisplayModeOutline()
356 {
357 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
358 canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
359 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
360 }
362 /**
363 * Returns current root (=bottom) layer.
364 */
365 SPObject *SPDesktop::currentRoot() const
366 {
367 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
368 }
370 /**
371 * Returns current top layer.
372 */
373 SPObject *SPDesktop::currentLayer() const
374 {
375 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
376 }
378 /**
379 * Sets the current layer of the desktop.
380 *
381 * Make \a object the top layer.
382 */
383 void SPDesktop::setCurrentLayer(SPObject *object) {
384 g_return_if_fail(SP_IS_GROUP(object));
385 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
386 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
387 _layer_hierarchy->setBottom(object);
388 }
390 /**
391 * Return layer that contains \a object.
392 */
393 SPObject *SPDesktop::layerForObject(SPObject *object) {
394 g_return_val_if_fail(object != NULL, NULL);
396 SPObject *root=currentRoot();
397 object = SP_OBJECT_PARENT(object);
398 while ( object && object != root && !isLayer(object) ) {
399 object = SP_OBJECT_PARENT(object);
400 }
401 return object;
402 }
404 /**
405 * True if object is a layer.
406 */
407 bool SPDesktop::isLayer(SPObject *object) const {
408 return ( SP_IS_GROUP(object)
409 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
410 == SPGroup::LAYER ) );
411 }
413 /**
414 * True if desktop viewport fully contains \a item's bbox.
415 */
416 bool SPDesktop::isWithinViewport (SPItem *item) const
417 {
418 NR::Rect const viewport = get_display_area();
419 NR::Rect const bbox = sp_item_bbox_desktop(item);
420 return viewport.contains(bbox);
421 }
423 ///
424 bool SPDesktop::itemIsHidden(SPItem const *item) const {
425 return item->isHidden(this->dkey);
426 }
428 /**
429 * Set activate property of desktop; emit signal if changed.
430 */
431 void
432 SPDesktop::set_active (bool new_active)
433 {
434 if (new_active != _active) {
435 _active = new_active;
436 if (new_active) {
437 _activate_signal.emit();
438 } else {
439 _deactivate_signal.emit();
440 }
441 }
442 }
444 /**
445 * Set activate status of current desktop's named view.
446 */
447 void
448 SPDesktop::activate_guides(bool activate)
449 {
450 guides_active = activate;
451 namedview->activateGuides(this, activate);
452 }
454 /**
455 * Make desktop switch documents.
456 */
457 void
458 SPDesktop::change_document (SPDocument *theDocument)
459 {
460 g_return_if_fail (theDocument != NULL);
462 /* unselect everything before switching documents */
463 selection->clear();
465 setDocument (theDocument);
466 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
467 _document_replaced_signal.emit (this, theDocument);
468 }
470 /**
471 * Make desktop switch event contexts.
472 */
473 void
474 SPDesktop::set_event_context (GtkType type, const gchar *config)
475 {
476 SPEventContext *ec;
477 while (event_context) {
478 ec = event_context;
479 sp_event_context_deactivate (ec);
480 event_context = ec->next;
481 sp_event_context_finish (ec);
482 g_object_unref (G_OBJECT (ec));
483 }
485 Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
486 ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
487 ec->next = event_context;
488 event_context = ec;
489 sp_event_context_activate (ec);
490 _event_context_changed_signal.emit (this, ec);
491 }
493 /**
494 * Push event context onto desktop's context stack.
495 */
496 void
497 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
498 {
499 SPEventContext *ref, *ec;
500 Inkscape::XML::Node *repr;
502 if (event_context && event_context->key == key) return;
503 ref = event_context;
504 while (ref && ref->next && ref->next->key != key) ref = ref->next;
505 if (ref && ref->next) {
506 ec = ref->next;
507 ref->next = ec->next;
508 sp_event_context_finish (ec);
509 g_object_unref (G_OBJECT (ec));
510 }
512 if (event_context) sp_event_context_deactivate (event_context);
513 repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
514 ec = sp_event_context_new (type, this, repr, key);
515 ec->next = event_context;
516 event_context = ec;
517 sp_event_context_activate (ec);
518 _event_context_changed_signal.emit (this, ec);
519 }
521 /**
522 * Sets the coordinate status to a given point
523 */
524 void
525 SPDesktop::set_coordinate_status (NR::Point p) {
526 _widget->setCoordinateStatus(p);
527 }
529 /**
530 * \see sp_document_item_from_list_at_point_bottom()
531 */
532 SPItem *
533 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
534 {
535 g_return_val_if_fail (doc() != NULL, NULL);
536 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
537 }
539 /**
540 * \see sp_document_item_at_point()
541 */
542 SPItem *
543 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
544 {
545 g_return_val_if_fail (doc() != NULL, NULL);
546 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
547 }
549 /**
550 * \see sp_document_group_at_point()
551 */
552 SPItem *
553 SPDesktop::group_at_point (NR::Point const p) const
554 {
555 g_return_val_if_fail (doc() != NULL, NULL);
556 return sp_document_group_at_point (doc(), dkey, p);
557 }
559 /**
560 * \brief Returns the mouse point in document coordinates; if mouse is
561 * outside the canvas, returns the center of canvas viewpoint
562 */
563 NR::Point
564 SPDesktop::point() const
565 {
566 NR::Point p = _widget->getPointer();
567 NR::Point pw = sp_canvas_window_to_world (canvas, p);
568 p = w2d(pw);
570 NR::Rect const r = canvas->getViewbox();
572 NR::Point r0 = w2d(r.min());
573 NR::Point r1 = w2d(r.max());
575 if (p[NR::X] >= r0[NR::X] &&
576 p[NR::X] <= r1[NR::X] &&
577 p[NR::Y] >= r1[NR::Y] &&
578 p[NR::Y] <= r0[NR::Y])
579 {
580 return p;
581 } else {
582 return (r0 + r1) / 2;
583 }
584 }
586 /**
587 * Put current zoom data in history list.
588 */
589 void
590 SPDesktop::push_current_zoom (GList **history)
591 {
592 NR::Rect const area = get_display_area();
594 NRRect *old_zoom = g_new(NRRect, 1);
595 old_zoom->x0 = area.min()[NR::X];
596 old_zoom->x1 = area.max()[NR::X];
597 old_zoom->y0 = area.min()[NR::Y];
598 old_zoom->y1 = area.max()[NR::Y];
599 if ( *history == NULL
600 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
601 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
602 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
603 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
604 {
605 *history = g_list_prepend (*history, old_zoom);
606 }
607 }
609 /**
610 * Set viewbox.
611 */
612 void
613 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
614 {
615 g_assert(_widget);
617 // save the zoom
618 if (log) {
619 push_current_zoom(&zooms_past);
620 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
621 g_list_free (zooms_future);
622 zooms_future = NULL;
623 }
625 double const cx = 0.5 * (x0 + x1);
626 double const cy = 0.5 * (y0 + y1);
628 NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
630 double scale = expansion(_d2w);
631 double newscale;
632 if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
633 newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
634 } else {
635 newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
636 }
638 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
640 int clear = FALSE;
641 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
642 /* Set zoom factors */
643 _d2w = NR::Matrix(NR::scale(newscale, -newscale));
644 _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
645 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
646 clear = TRUE;
647 }
649 /* Calculate top left corner */
650 x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
651 y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
653 /* Scroll */
654 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
656 _widget->updateRulers();
657 _widget->updateScrollbars(expansion(_d2w));
658 _widget->updateZoom();
659 }
661 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
662 {
663 set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
664 }
666 /**
667 * Return viewbox dimensions.
668 */
669 NR::Rect SPDesktop::get_display_area() const
670 {
671 NR::Rect const viewbox = canvas->getViewbox();
673 double const scale = _d2w[0];
675 return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
676 NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
677 }
679 /**
680 * Revert back to previous zoom if possible.
681 */
682 void
683 SPDesktop::prev_zoom()
684 {
685 if (zooms_past == NULL) {
686 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
687 return;
688 }
690 // push current zoom into forward zooms list
691 push_current_zoom (&zooms_future);
693 // restore previous zoom
694 set_display_area (((NRRect *) zooms_past->data)->x0,
695 ((NRRect *) zooms_past->data)->y0,
696 ((NRRect *) zooms_past->data)->x1,
697 ((NRRect *) zooms_past->data)->y1,
698 0, false);
700 // remove the just-added zoom from the past zooms list
701 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
702 }
704 /**
705 * Set zoom to next in list.
706 */
707 void
708 SPDesktop::next_zoom()
709 {
710 if (zooms_future == NULL) {
711 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
712 return;
713 }
715 // push current zoom into past zooms list
716 push_current_zoom (&zooms_past);
718 // restore next zoom
719 set_display_area (((NRRect *) zooms_future->data)->x0,
720 ((NRRect *) zooms_future->data)->y0,
721 ((NRRect *) zooms_future->data)->x1,
722 ((NRRect *) zooms_future->data)->y1,
723 0, false);
725 // remove the just-used zoom from the zooms_future list
726 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
727 }
729 /**
730 * Zoom to point with absolute zoom factor.
731 */
732 void
733 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
734 {
735 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
737 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
738 // this check prevents "sliding" when trying to zoom in at maximum zoom;
739 /// \todo someone please fix calculations properly and remove this hack
740 if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
741 return;
743 NR::Rect const viewbox = canvas->getViewbox();
745 double const width2 = viewbox.dimensions()[NR::X] / zoom;
746 double const height2 = viewbox.dimensions()[NR::Y] / zoom;
748 set_display_area(cx - px * width2,
749 cy - py * height2,
750 cx + (1 - px) * width2,
751 cy + (1 - py) * height2,
752 0.0);
753 }
755 /**
756 * Zoom to center with absolute zoom factor.
757 */
758 void
759 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
760 {
761 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
762 }
764 /**
765 * Zoom to point with relative zoom factor.
766 */
767 void
768 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
769 {
770 NR::Rect const area = get_display_area();
772 if (cx < area.min()[NR::X]) {
773 cx = area.min()[NR::X];
774 }
775 if (cx > area.max()[NR::X]) {
776 cx = area.max()[NR::X];
777 }
778 if (cy < area.min()[NR::Y]) {
779 cy = area.min()[NR::Y];
780 }
781 if (cy > area.max()[NR::Y]) {
782 cy = area.max()[NR::Y];
783 }
785 gdouble const scale = expansion(_d2w) * zoom;
786 double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
787 double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
789 zoom_absolute_keep_point(cx, cy, px, py, scale);
790 }
792 /**
793 * Zoom to center with relative zoom factor.
794 */
795 void
796 SPDesktop::zoom_relative (double cx, double cy, double zoom)
797 {
798 gdouble scale = expansion(_d2w) * zoom;
799 zoom_absolute (cx, cy, scale);
800 }
802 /**
803 * Set display area to origin and current document dimensions.
804 */
805 void
806 SPDesktop::zoom_page()
807 {
808 NR::Rect d(NR::Point(0, 0),
809 NR::Point(sp_document_width(doc()), sp_document_height(doc())));
811 if (d.dimensions()[NR::X] < 1.0 || d.dimensions()[NR::Y] < 1.0) {
812 return;
813 }
815 set_display_area(d, 10);
816 }
818 /**
819 * Set display area to current document width.
820 */
821 void
822 SPDesktop::zoom_page_width()
823 {
824 NR::Rect const a = get_display_area();
826 if (sp_document_width(doc()) < 1.0) {
827 return;
828 }
830 NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
831 NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
833 set_display_area(d, 10);
834 }
836 /**
837 * Zoom to selection.
838 */
839 void
840 SPDesktop::zoom_selection()
841 {
842 NR::Rect const d = selection->bounds();
844 if (d.dimensions()[NR::X] < 0.1 || d.dimensions()[NR::Y] < 0.1) {
845 return;
846 }
848 set_display_area(d, 10);
849 }
851 /**
852 * Tell widget to let zoom widget grab keyboard focus.
853 */
854 void
855 SPDesktop::zoom_grab_focus()
856 {
857 _widget->letZoomGrabFocus();
858 }
860 /**
861 * Zoom to whole drawing.
862 */
863 void
864 SPDesktop::zoom_drawing()
865 {
866 g_return_if_fail (doc() != NULL);
867 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
868 g_return_if_fail (docitem != NULL);
870 NR::Rect d = sp_item_bbox_desktop(docitem);
872 /* Note that the second condition here indicates that
873 ** there are no items in the drawing.
874 */
875 if ( d.dimensions()[NR::X] < 1.0 || d.dimensions()[NR::Y] < 1.0 ) {
876 return;
877 }
879 set_display_area(d, 10);
880 }
882 /**
883 * Scroll canvas by specific coordinate amount.
884 */
885 void
886 SPDesktop::scroll_world (double dx, double dy)
887 {
888 g_assert(_widget);
890 NR::Rect const viewbox = canvas->getViewbox();
892 sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE);
894 _widget->updateRulers();
895 _widget->updateScrollbars(expansion(_d2w));
896 }
898 bool
899 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
900 {
901 gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
903 // autoscrolldistance is in screen pixels, but the display area is in document units
904 autoscrolldistance /= expansion(_d2w);
905 NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
907 if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
908 !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y]) ) {
910 NR::Point const s_w( (*p) * _d2w );
912 gdouble x_to;
913 if ((*p)[NR::X] < dbox.min()[NR::X])
914 x_to = dbox.min()[NR::X];
915 else if ((*p)[NR::X] > dbox.max()[NR::X])
916 x_to = dbox.max()[NR::X];
917 else
918 x_to = (*p)[NR::X];
920 gdouble y_to;
921 if ((*p)[NR::Y] < dbox.min()[NR::Y])
922 y_to = dbox.min()[NR::Y];
923 else if ((*p)[NR::Y] > dbox.max()[NR::Y])
924 y_to = dbox.max()[NR::Y];
925 else
926 y_to = (*p)[NR::Y];
928 NR::Point const d_dt(x_to, y_to);
929 NR::Point const d_w( d_dt * _d2w );
930 NR::Point const moved_w( d_w - s_w );
932 if (autoscrollspeed == 0)
933 autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
935 if (autoscrollspeed != 0)
936 scroll_world (autoscrollspeed * moved_w);
938 return true;
939 }
940 return false;
941 }
943 void
944 SPDesktop::fullscreen()
945 {
946 _widget->setFullscreen();
947 }
949 void
950 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
951 {
952 _widget->getGeometry (x, y, w, h);
953 }
955 void
956 SPDesktop::setWindowPosition (NR::Point p)
957 {
958 _widget->setPosition (p);
959 }
961 void
962 SPDesktop::setWindowSize (gint w, gint h)
963 {
964 _widget->setSize (w, h);
965 }
967 void
968 SPDesktop::setWindowTransient (void *p, int transient_policy)
969 {
970 _widget->setTransient (p, transient_policy);
971 }
973 void
974 SPDesktop::presentWindow()
975 {
976 _widget->present();
977 }
979 bool
980 SPDesktop::warnDialog (gchar *text)
981 {
982 return _widget->warnDialog (text);
983 }
985 void
986 SPDesktop::toggleRulers()
987 {
988 _widget->toggleRulers();
989 }
991 void
992 SPDesktop::toggleScrollbars()
993 {
994 _widget->toggleScrollbars();
995 }
997 void
998 SPDesktop::layoutWidget()
999 {
1000 _widget->layout();
1001 }
1003 void
1004 SPDesktop::destroyWidget()
1005 {
1006 _widget->destroy();
1007 }
1009 bool
1010 SPDesktop::shutdown()
1011 {
1012 return _widget->shutdown();
1013 }
1015 void
1016 SPDesktop::setToolboxFocusTo (gchar const *label)
1017 {
1018 _widget->setToolboxFocusTo (label);
1019 }
1021 void
1022 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1023 {
1024 _widget->setToolboxAdjustmentValue (id, val);
1025 }
1027 bool
1028 SPDesktop::isToolboxButtonActive (gchar const *id)
1029 {
1030 return _widget->isToolboxButtonActive (id);
1031 }
1033 void
1034 SPDesktop::emitToolSubselectionChanged(gpointer data)
1035 {
1036 _tool_subselection_changed.emit(data);
1037 inkscape_subselection_changed (this);
1038 }
1040 void
1041 SPDesktop::updateNow()
1042 {
1043 sp_canvas_update_now(canvas);
1044 }
1046 void
1047 SPDesktop::enableInteraction()
1048 {
1049 _widget->enableInteraction();
1050 }
1052 void SPDesktop::disableInteraction()
1053 {
1054 _widget->disableInteraction();
1055 }
1057 //----------------------------------------------------------------------
1058 // Callback implementations. The virtual ones are connected by the view.
1060 void
1061 SPDesktop::onPositionSet (double x, double y)
1062 {
1063 _widget->viewSetPosition (NR::Point(x,y));
1064 }
1066 void
1067 SPDesktop::onResized (double x, double y)
1068 {
1069 // Nothing called here
1070 }
1072 /**
1073 * Redraw callback; queues Gtk redraw; connected by View.
1074 */
1075 void
1076 SPDesktop::onRedrawRequested ()
1077 {
1078 if (main) {
1079 _widget->requestCanvasUpdate();
1080 }
1081 }
1083 void
1084 SPDesktop::updateCanvasNow()
1085 {
1086 _widget->requestCanvasUpdateAndWait();
1087 }
1089 /**
1090 * Associate document with desktop.
1091 */
1092 /// \todo fixme: refactor SPDesktop::init to use setDocument
1093 void
1094 SPDesktop::setDocument (SPDocument *doc)
1095 {
1096 if (this->doc() && doc) {
1097 namedview->hide(this);
1098 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1099 }
1101 if (_layer_hierarchy) {
1102 _layer_hierarchy->clear();
1103 delete _layer_hierarchy;
1104 }
1105 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1106 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1107 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1108 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1109 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1111 /* setup EventLog */
1112 event_log = new Inkscape::EventLog(doc);
1113 doc->addUndoObserver(*event_log);
1115 _commit_connection.disconnect();
1116 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1118 /// \todo fixme: This condition exists to make sure the code
1119 /// inside is called only once on initialization. But there
1120 /// are surely more safe methods to accomplish this.
1121 if (drawing) {
1122 NRArenaItem *ai;
1124 namedview = sp_document_namedview (doc, NULL);
1125 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1126 number = namedview->getViewCount();
1128 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1129 SP_CANVAS_ARENA (drawing)->arena,
1130 dkey,
1131 SP_ITEM_SHOW_DISPLAY);
1132 if (ai) {
1133 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1134 nr_arena_item_unref (ai);
1135 }
1136 namedview->show(this);
1137 /* Ugly hack */
1138 activate_guides (true);
1139 /* Ugly hack */
1140 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1141 }
1143 _document_replaced_signal.emit (this, doc);
1145 View::setDocument (doc);
1146 }
1148 void
1149 SPDesktop::onStatusMessage
1150 (Inkscape::MessageType type, gchar const *message)
1151 {
1152 if (_widget) {
1153 _widget->setMessage(type, message);
1154 }
1155 }
1157 void
1158 SPDesktop::onDocumentURISet (gchar const* uri)
1159 {
1160 _widget->setTitle(uri);
1161 }
1163 /**
1164 * Resized callback.
1165 */
1166 void
1167 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1168 {
1169 _doc2dt[5] = height;
1170 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1171 NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1172 SP_CTRLRECT(page)->setRectangle(a);
1173 SP_CTRLRECT(page_border)->setRectangle(a);
1174 }
1177 void
1178 SPDesktop::_onActivate (SPDesktop* dt)
1179 {
1180 if (!dt->_widget) return;
1181 dt->_widget->activateDesktop();
1182 }
1184 void
1185 SPDesktop::_onDeactivate (SPDesktop* dt)
1186 {
1187 if (!dt->_widget) return;
1188 dt->_widget->deactivateDesktop();
1189 }
1191 void
1192 SPDesktop::_onSelectionModified
1193 (Inkscape::Selection *selection, guint flags, SPDesktop *dt)
1194 {
1195 if (!dt->_widget) return;
1196 dt->_widget->updateScrollbars (expansion(dt->_d2w));
1197 }
1199 static void
1200 _onSelectionChanged
1201 (Inkscape::Selection *selection, SPDesktop *desktop)
1202 {
1203 /** \todo
1204 * only change the layer for single selections, or what?
1205 * This seems reasonable -- for multiple selections there can be many
1206 * different layers involved.
1207 */
1208 SPItem *item=selection->singleItem();
1209 if (item) {
1210 SPObject *layer=desktop->layerForObject(item);
1211 if ( layer && layer != desktop->currentLayer() ) {
1212 desktop->setCurrentLayer(layer);
1213 }
1214 }
1215 }
1217 /**
1218 * Calls event handler of current event context.
1219 * \param arena Unused
1220 * \todo fixme
1221 */
1222 static gint
1223 _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1224 {
1225 if (ai) {
1226 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1227 return sp_event_context_item_handler (desktop->event_context, spi, event);
1228 } else {
1229 return sp_event_context_root_handler (desktop->event_context, event);
1230 }
1231 }
1233 static void
1234 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1235 g_return_if_fail(SP_IS_GROUP(layer));
1236 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1237 }
1239 /// Callback
1240 static void
1241 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1242 g_return_if_fail(SP_IS_GROUP(layer));
1243 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1244 }
1246 /// Callback
1247 static void
1248 _layer_hierarchy_changed(SPObject *top, SPObject *bottom,
1249 SPDesktop *desktop)
1250 {
1251 desktop->_layer_changed_signal.emit (bottom);
1252 }
1254 /// Called when document is starting to be rebuilt.
1255 static void
1256 _reconstruction_start (SPDesktop * desktop)
1257 {
1258 // printf("Desktop, starting reconstruction\n");
1259 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1260 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1262 /*
1263 GSList const * selection_objs = desktop->selection->list();
1264 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1266 }
1267 */
1268 desktop->selection->clear();
1270 // printf("Desktop, starting reconstruction end\n");
1271 }
1273 /// Called when document rebuild is finished.
1274 static void
1275 _reconstruction_finish (SPDesktop * desktop)
1276 {
1277 // printf("Desktop, finishing reconstruction\n");
1278 if (desktop->_reconstruction_old_layer_id == NULL)
1279 return;
1281 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1282 if (newLayer != NULL)
1283 desktop->setCurrentLayer(newLayer);
1285 g_free(desktop->_reconstruction_old_layer_id);
1286 desktop->_reconstruction_old_layer_id = NULL;
1287 // printf("Desktop, finishing reconstruction end\n");
1288 return;
1289 }
1291 /**
1292 * Namedview_modified callback.
1293 */
1294 static void
1295 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1296 {
1297 SPNamedView *nv=SP_NAMEDVIEW(obj);
1299 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1301 /* Recalculate snap distances */
1302 /* FIXME: why is the desktop getting involved in setting up something
1303 ** that is entirely to do with the namedview?
1304 */
1305 _update_snap_distances (desktop);
1307 /* Show/hide page background */
1308 if (nv->pagecolor & 0xff) {
1309 sp_canvas_item_show (desktop->table);
1310 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1311 sp_canvas_item_move_to_z (desktop->table, 0);
1312 } else {
1313 sp_canvas_item_hide (desktop->table);
1314 }
1316 /* Show/hide page border */
1317 if (nv->showborder) {
1318 // show
1319 sp_canvas_item_show (desktop->page_border);
1320 // set color and shadow
1321 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1322 if (nv->pageshadow) {
1323 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1324 }
1325 // place in the z-order stack
1326 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1327 sp_canvas_item_move_to_z (desktop->page_border, 2);
1328 } else {
1329 int order = sp_canvas_item_order (desktop->page_border);
1330 int morder = sp_canvas_item_order (desktop->drawing);
1331 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1332 morder - order);
1333 }
1334 } else {
1335 sp_canvas_item_hide (desktop->page_border);
1336 if (nv->pageshadow) {
1337 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1338 }
1339 }
1341 /* Show/hide page shadow */
1342 if (nv->showpageshadow && nv->pageshadow) {
1343 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1344 } else {
1345 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1346 }
1348 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1349 (SP_RGBA32_R_U(nv->pagecolor) +
1350 SP_RGBA32_G_U(nv->pagecolor) +
1351 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1352 // the background color is light or transparent, use black outline
1353 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1354 } else { // use white outline
1355 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1356 }
1357 }
1358 }
1360 /**
1361 * Callback to reset snapper's distances.
1362 */
1363 static void
1364 _update_snap_distances (SPDesktop *desktop)
1365 {
1366 SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1368 SPNamedView &nv = *desktop->namedview;
1370 nv.snap_manager.grid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1371 *nv.gridtoleranceunit,
1372 px));
1373 nv.snap_manager.axonomgrid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1374 *nv.gridtoleranceunit,
1375 px));
1376 nv.snap_manager.guide.setDistance(sp_convert_distance_full(nv.guidetolerance,
1377 *nv.guidetoleranceunit,
1378 px));
1379 nv.snap_manager.object.setDistance(sp_convert_distance_full(nv.objecttolerance,
1380 *nv.objecttoleranceunit,
1381 px));
1382 }
1385 NR::Matrix SPDesktop::w2d() const
1386 {
1387 return _w2d;
1388 }
1390 NR::Point SPDesktop::w2d(NR::Point const &p) const
1391 {
1392 return p * _w2d;
1393 }
1395 NR::Point SPDesktop::d2w(NR::Point const &p) const
1396 {
1397 return p * _d2w;
1398 }
1400 NR::Matrix SPDesktop::doc2dt() const
1401 {
1402 return _doc2dt;
1403 }
1405 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1406 {
1407 return p * _doc2dt;
1408 }
1410 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1411 {
1412 return p / _doc2dt;
1413 }
1416 /**
1417 * Pop event context from desktop's context stack. Never used.
1418 */
1419 // void
1420 // SPDesktop::pop_event_context (unsigned int key)
1421 // {
1422 // SPEventContext *ec = NULL;
1423 //
1424 // if (event_context && event_context->key == key) {
1425 // g_return_if_fail (event_context);
1426 // g_return_if_fail (event_context->next);
1427 // ec = event_context;
1428 // sp_event_context_deactivate (ec);
1429 // event_context = ec->next;
1430 // sp_event_context_activate (event_context);
1431 // _event_context_changed_signal.emit (this, ec);
1432 // }
1433 //
1434 // SPEventContext *ref = event_context;
1435 // while (ref && ref->next && ref->next->key != key)
1436 // ref = ref->next;
1437 //
1438 // if (ref && ref->next) {
1439 // ec = ref->next;
1440 // ref->next = ec->next;
1441 // }
1442 //
1443 // if (ec) {
1444 // sp_event_context_finish (ec);
1445 // g_object_unref (G_OBJECT (ec));
1446 // }
1447 // }
1449 /*
1450 Local Variables:
1451 mode:c++
1452 c-file-style:"stroustrup"
1453 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1454 indent-tabs-mode:nil
1455 fill-column:99
1456 End:
1457 */
1458 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :