Code

Monster commit. New grid infrastructure. The old gridmanagement code is still there...
[inkscape.git] / src / desktop.cpp
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  *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
13  *
14  * Copyright (C) 2006-2007 Johan Engelen
15  * Copyright (C) 2006 John Bintz
16  * Copyright (C) 2004 MenTaLguY
17  * Copyright (C) 1999-2002 Lauris Kaplinski
18  * Copyright (C) 2000-2001 Ximian, Inc.
19  *
20  * Released under GNU GPL, read the file 'COPYING' for more information
21  */
23 /** \class SPDesktop
24  * SPDesktop is a subclass of View, implementing an editable document
25  * canvas.  It is extensively used by many UI controls that need certain
26  * visual representations of their own.
27  *
28  * SPDesktop provides a certain set of SPCanvasItems, serving as GUI
29  * layers of different control objects. The one containing the whole
30  * document is the drawing layer. In addition to it, there are grid,
31  * guide, sketch and control layers. The sketch layer is used for
32  * temporary drawing objects, before the real objects in document are
33  * created. The control layer contains editing knots, rubberband and
34  * similar non-document UI objects.
35  *
36  * Each SPDesktop is associated with a SPNamedView node of the document
37  * tree.  Currently, all desktops are created from a single main named
38  * view, but in the future there may be support for different ones.
39  * SPNamedView serves as an in-document container for desktop-related
40  * data, like grid and guideline placement, snapping options and so on.
41  *
42  * Associated with each SPDesktop are the two most important editing
43  * related objects - SPSelection and SPEventContext.
44  *
45  * Sodipodi keeps track of the active desktop and invokes notification
46  * signals whenever it changes. UI elements can use these to update their
47  * display to the selection of the currently active editing window.
48  * (Lauris Kaplinski)
49  */
51 #ifdef HAVE_CONFIG_H
52 # include "config.h"
53 #endif
55 #include <glibmm/i18n.h>
56 #include <sigc++/functors/mem_fun.h>
57 #include <gtkmm.h>
59 #include "macros.h"
60 #include "inkscape-private.h"
61 #include "desktop.h"
62 #include "desktop-events.h"
63 #include "desktop-handles.h"
64 #include "document.h"
65 #include "message-stack.h"
66 #include "selection.h"
67 #include "select-context.h"
68 #include "sp-namedview.h"
69 #include "color.h"
70 #include "sp-item-group.h"
71 #include "prefs-utils.h"
72 #include "object-hierarchy.h"
73 #include "helper/units.h"
74 #include "display/canvas-arena.h"
75 #include "display/nr-arena.h"
76 #include "display/gnome-canvas-acetate.h"
77 #include "display/sodipodi-ctrlrect.h"
78 #include "display/sp-canvas-util.h"
79 #include "libnr/nr-matrix-div.h"
80 #include "libnr/nr-rect-ops.h"
81 #include "ui/dialog/dialog-manager.h"
82 #include "xml/repr.h"
83 #include "message-context.h"
84 #include "layer-manager.h"
85 #include "event-log.h"
86 #include "display/canvas-grid.h"
88 namespace Inkscape { namespace XML { class Node; }}
90 // Callback declarations
91 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
92 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
93 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
94 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
95 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
96 static void _reconstruction_start(SPDesktop * desktop);
97 static void _reconstruction_finish(SPDesktop * desktop);
98 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
99 static void _update_snap_distances (SPDesktop *desktop);
101 /**
102  * Return new desktop object.
103  * \pre namedview != NULL.
104  * \pre canvas != NULL.
105  */
106 SPDesktop::SPDesktop()
108     _dlg_mgr = NULL;
109     _widget = 0;
110     namedview = NULL;
111     selection = NULL;
112     acetate = NULL;
113     main = NULL;
114     grid = NULL;
115     guides = NULL;
116     drawing = NULL;
117     sketch = NULL;
118     controls = NULL;
119     event_context = 0;
120     layer_manager = 0;
122     _d2w.set_identity();
123     _w2d.set_identity();
124     _doc2dt = NR::Matrix(NR::scale(1, -1));
126     guides_active = false;
128     zooms_past = NULL;
129     zooms_future = NULL;
131     is_fullscreen = false;
132     waiting_cursor = false;
134     gr_item = NULL;
135     gr_point_type = 0;
136     gr_point_i = 0;
137     gr_fill_or_stroke = true;
139     _layer_hierarchy = NULL;
140     _active = false;
142     selection = Inkscape::GC::release (new Inkscape::Selection (this));
145 void
146 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
149     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
151     current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
153     namedview = nv;
154     canvas = aCanvas;
156     SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
157     /* Kill flicker */
158     sp_document_ensure_up_to_date (document);
160     /* Setup Dialog Manager */
161     _dlg_mgr = new Inkscape::UI::Dialog::DialogManager();
163     dkey = sp_item_display_key_new (1);
165     /* Connect document */
166     setDocument (document);
168     number = namedview->getViewCount();
171     /* Setup Canvas */
172     g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
174     SPCanvasGroup *root = sp_canvas_root (canvas);
176     /* Setup adminstrative layers */
177     acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
178     g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
179     main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
180     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
182     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
183     SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
184     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
185     sp_canvas_item_move_to_z (table, 0);
187     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
188     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
189     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
191     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
192     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
194     SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
196     if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
197         // Start in outline mode
198         setDisplayModeOutline();
199     } else {
200         // Start in normal mode, default
201         setDisplayModeNormal();
202     }
204     grid = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
205     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
206     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
207     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
209     /* Push select tool to the bottom of stack */
210     /** \todo
211      * FIXME: this is the only call to this.  Everything else seems to just
212      * call "set" instead of "push".  Can we assume that there is only one
213      * context ever?
214      */
215     push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
217     // display rect and zoom are now handled in sp_desktop_widget_realize()
219     NR::Rect const d(NR::Point(0.0, 0.0),
220                      NR::Point(sp_document_width(document), sp_document_height(document)));
222     SP_CTRLRECT(page)->setRectangle(d);
223     SP_CTRLRECT(page_border)->setRectangle(d);
225     /* the following sets the page shadow on the canvas
226        It was originally set to 5, which is really cheesy!
227        It now is an attribute in the document's namedview. If a value of
228        0 is used, then the constructor for a shadow is not initialized.
229     */
231     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
232         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
233     }
236     /* Connect event for page resize */
237     _doc2dt[5] = sp_document_height (document);
238     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
240     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
242     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
243             SP_CANVAS_ARENA (drawing)->arena,
244             dkey,
245             SP_ITEM_SHOW_DISPLAY);
246     if (ai) {
247         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
248         nr_arena_item_unref (ai);
249     }
251     namedview->show(this);
252     /* Ugly hack */
253     activate_guides (true);
254     /* Ugly hack */
255     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
257 /* Set up notification of rebuilding the document, this allows
258        for saving object related settings in the document. */
259     _reconstruction_start_connection =
260         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
261     _reconstruction_finish_connection =
262         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
263     _reconstruction_old_layer_id = NULL;
264     
265     _commit_connection = document->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
266     
267     // ?
268     // sp_active_desktop_set (desktop);
269     _inkscape = INKSCAPE;
271     _activate_connection = _activate_signal.connect(
272         sigc::bind(
273             sigc::ptr_fun(_onActivate),
274             this
275         )
276     );
277      _deactivate_connection = _deactivate_signal.connect(
278         sigc::bind(
279             sigc::ptr_fun(_onDeactivate),
280             this
281         )
282     );
284     _sel_modified_connection = selection->connectModified(
285         sigc::bind(
286             sigc::ptr_fun(&_onSelectionModified),
287             this
288         )
289     );
290     _sel_changed_connection = selection->connectChanged(
291         sigc::bind(
292             sigc::ptr_fun(&_onSelectionChanged),
293             this
294         )
295     );
298     /* setup LayerManager */
299     //   (Setting up after the connections are all in place, as it may use some of them)
300     layer_manager = new Inkscape::LayerManager( this );
304 void SPDesktop::destroy()
306     _activate_connection.disconnect();
307     _deactivate_connection.disconnect();
308     _sel_modified_connection.disconnect();
309     _sel_changed_connection.disconnect();
310     _modified_connection.disconnect();
311     _commit_connection.disconnect();
312     _reconstruction_start_connection.disconnect();
313     _reconstruction_finish_connection.disconnect();
315     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
316     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
317     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
319     while (event_context) {
320         SPEventContext *ec = event_context;
321         event_context = ec->next;
322         sp_event_context_finish (ec);
323         g_object_unref (G_OBJECT (ec));
324     }
326     if (_layer_hierarchy) {
327         delete _layer_hierarchy;
328     }
330     if (_inkscape) {
331         _inkscape = NULL;
332     }
334     if (drawing) {
335         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
336         drawing = NULL;
337     }
339     delete _guides_message_context;
340     _guides_message_context = NULL;
342     g_list_free (zooms_past);
343     g_list_free (zooms_future);
346 SPDesktop::~SPDesktop() {}
348 //--------------------------------------------------------------------
349 /* Public methods */
351 void SPDesktop::setDisplayModeNormal()
353     SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
354     canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
355     displayMode = RENDERMODE_NORMAL;
356     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
357     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
360 void SPDesktop::setDisplayModeOutline()
362     SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
363     canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
364     displayMode = RENDERMODE_OUTLINE;
365     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
366     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
369 void SPDesktop::displayModeToggle()
371     if (displayMode == RENDERMODE_OUTLINE)
372         setDisplayModeNormal();
373     else 
374         setDisplayModeOutline();
377 /**
378  * Returns current root (=bottom) layer.
379  */
380 SPObject *SPDesktop::currentRoot() const
382     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
385 /**
386  * Returns current top layer.
387  */
388 SPObject *SPDesktop::currentLayer() const
390     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
393 /**
394  * Sets the current layer of the desktop.
395  * 
396  * Make \a object the top layer.
397  */
398 void SPDesktop::setCurrentLayer(SPObject *object) {
399     g_return_if_fail(SP_IS_GROUP(object));
400     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
401     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
402     _layer_hierarchy->setBottom(object);
405 /**
406  * Return layer that contains \a object.
407  */
408 SPObject *SPDesktop::layerForObject(SPObject *object) {
409     g_return_val_if_fail(object != NULL, NULL);
411     SPObject *root=currentRoot();
412     object = SP_OBJECT_PARENT(object);
413     while ( object && object != root && !isLayer(object) ) {
414         object = SP_OBJECT_PARENT(object);
415     }
416     return object;
419 /**
420  * True if object is a layer.
421  */
422 bool SPDesktop::isLayer(SPObject *object) const {
423     return ( SP_IS_GROUP(object)
424              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
425                   == SPGroup::LAYER ) );
428 /**
429  * True if desktop viewport fully contains \a item's bbox.
430  */
431 bool SPDesktop::isWithinViewport (SPItem *item) const
433     NR::Rect const viewport = get_display_area();
434     NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
435     if (bbox) {
436         return viewport.contains(*bbox);
437     } else {
438         return true;
439     }
442 ///
443 bool SPDesktop::itemIsHidden(SPItem const *item) const {
444     return item->isHidden(this->dkey);
447 /**
448  * Set activate property of desktop; emit signal if changed.
449  */
450 void
451 SPDesktop::set_active (bool new_active)
453     if (new_active != _active) {
454         _active = new_active;
455         if (new_active) {
456             _activate_signal.emit();
457         } else {
458             _deactivate_signal.emit();
459         }
460     }
463 /**
464  * Set activate status of current desktop's named view.
465  */
466 void
467 SPDesktop::activate_guides(bool activate)
469     guides_active = activate;
470     namedview->activateGuides(this, activate);
473 /**
474  * Make desktop switch documents.
475  */
476 void
477 SPDesktop::change_document (SPDocument *theDocument)
479     g_return_if_fail (theDocument != NULL);
481     /* unselect everything before switching documents */
482     selection->clear();
484     setDocument (theDocument);
485     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
486     _document_replaced_signal.emit (this, theDocument);
489 /**
490  * Make desktop switch event contexts.
491  */
492 void
493 SPDesktop::set_event_context (GtkType type, const gchar *config)
495     SPEventContext *ec;
496     while (event_context) {
497         ec = event_context;
498         sp_event_context_deactivate (ec);
499         event_context = ec->next;
500         sp_event_context_finish (ec);
501         g_object_unref (G_OBJECT (ec));
502     }
504     Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
505     ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
506     ec->next = event_context;
507     event_context = ec;
508     sp_event_context_activate (ec);
509     _event_context_changed_signal.emit (this, ec);
512 /**
513  * Push event context onto desktop's context stack.
514  */
515 void
516 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
518     SPEventContext *ref, *ec;
519     Inkscape::XML::Node *repr;
521     if (event_context && event_context->key == key) return;
522     ref = event_context;
523     while (ref && ref->next && ref->next->key != key) ref = ref->next;
524     if (ref && ref->next) {
525         ec = ref->next;
526         ref->next = ec->next;
527         sp_event_context_finish (ec);
528         g_object_unref (G_OBJECT (ec));
529     }
531     if (event_context) sp_event_context_deactivate (event_context);
532     repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
533     ec = sp_event_context_new (type, this, repr, key);
534     ec->next = event_context;
535     event_context = ec;
536     sp_event_context_activate (ec);
537     _event_context_changed_signal.emit (this, ec);
540 /**
541  * Sets the coordinate status to a given point
542  */
543 void
544 SPDesktop::set_coordinate_status (NR::Point p) {
545     _widget->setCoordinateStatus(p);
548 /**
549  * \see sp_document_item_from_list_at_point_bottom()
550  */
551 SPItem *
552 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
554     g_return_val_if_fail (doc() != NULL, NULL);
555     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
558 /**
559  * \see sp_document_item_at_point()
560  */
561 SPItem *
562 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
564     g_return_val_if_fail (doc() != NULL, NULL);
565     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
568 /**
569  * \see sp_document_group_at_point()
570  */
571 SPItem *
572 SPDesktop::group_at_point (NR::Point const p) const
574     g_return_val_if_fail (doc() != NULL, NULL);
575     return sp_document_group_at_point (doc(), dkey, p);
578 /**
579  * \brief  Returns the mouse point in document coordinates; if mouse is
580  * outside the canvas, returns the center of canvas viewpoint
581  */
582 NR::Point
583 SPDesktop::point() const
585     NR::Point p = _widget->getPointer();
586     NR::Point pw = sp_canvas_window_to_world (canvas, p);
587     p = w2d(pw);
589     NR::Rect const r = canvas->getViewbox();
591     NR::Point r0 = w2d(r.min());
592     NR::Point r1 = w2d(r.max());
594     if (p[NR::X] >= r0[NR::X] &&
595         p[NR::X] <= r1[NR::X] &&
596         p[NR::Y] >= r1[NR::Y] &&
597         p[NR::Y] <= r0[NR::Y])
598     {
599         return p;
600     } else {
601         return (r0 + r1) / 2;
602     }
605 /**
606  * Put current zoom data in history list.
607  */
608 void
609 SPDesktop::push_current_zoom (GList **history)
611     NR::Rect const area = get_display_area();
613     NRRect *old_zoom = g_new(NRRect, 1);
614     old_zoom->x0 = area.min()[NR::X];
615     old_zoom->x1 = area.max()[NR::X];
616     old_zoom->y0 = area.min()[NR::Y];
617     old_zoom->y1 = area.max()[NR::Y];
618     if ( *history == NULL
619          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
620                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
621                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
622                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
623     {
624         *history = g_list_prepend (*history, old_zoom);
625     }
628 /**
629  * Set viewbox.
630  */
631 void
632 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
634     g_assert(_widget);
636     // save the zoom
637     if (log) {
638         push_current_zoom(&zooms_past);
639         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
640         g_list_free (zooms_future);
641         zooms_future = NULL;
642     }
644     double const cx = 0.5 * (x0 + x1);
645     double const cy = 0.5 * (y0 + y1);
647     NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
649     double scale = expansion(_d2w);
650     double newscale;
651     if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
652         newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
653     } else {
654         newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
655     }
657     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
659     int clear = FALSE;
660     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
661         /* Set zoom factors */
662         _d2w = NR::Matrix(NR::scale(newscale, -newscale));
663         _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
664         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
665         clear = TRUE;
666     }
668     /* Calculate top left corner */
669     x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
670     y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
672     /* Scroll */
673     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
675     _widget->updateRulers();
676     _widget->updateScrollbars(expansion(_d2w));
677     _widget->updateZoom();
680 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
682     set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
685 /**
686  * Return viewbox dimensions.
687  */
688 NR::Rect SPDesktop::get_display_area() const
690     NR::Rect const viewbox = canvas->getViewbox();
692     double const scale = _d2w[0];
694     return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
695                     NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
698 /**
699  * Revert back to previous zoom if possible.
700  */
701 void
702 SPDesktop::prev_zoom()
704     if (zooms_past == NULL) {
705         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
706         return;
707     }
709     // push current zoom into forward zooms list
710     push_current_zoom (&zooms_future);
712     // restore previous zoom
713     set_display_area (((NRRect *) zooms_past->data)->x0,
714             ((NRRect *) zooms_past->data)->y0,
715             ((NRRect *) zooms_past->data)->x1,
716             ((NRRect *) zooms_past->data)->y1,
717             0, false);
719     // remove the just-added zoom from the past zooms list
720     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
723 /**
724  * Set zoom to next in list.
725  */
726 void
727 SPDesktop::next_zoom()
729     if (zooms_future == NULL) {
730         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
731         return;
732     }
734     // push current zoom into past zooms list
735     push_current_zoom (&zooms_past);
737     // restore next zoom
738     set_display_area (((NRRect *) zooms_future->data)->x0,
739             ((NRRect *) zooms_future->data)->y0,
740             ((NRRect *) zooms_future->data)->x1,
741             ((NRRect *) zooms_future->data)->y1,
742             0, false);
744     // remove the just-used zoom from the zooms_future list
745     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
748 /**
749  * Zoom to point with absolute zoom factor.
750  */
751 void
752 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
754     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
756     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
757     // this check prevents "sliding" when trying to zoom in at maximum zoom;
758     /// \todo someone please fix calculations properly and remove this hack
759     if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
760         return;
762     NR::Rect const viewbox = canvas->getViewbox();
764     double const width2 = viewbox.dimensions()[NR::X] / zoom;
765     double const height2 = viewbox.dimensions()[NR::Y] / zoom;
767     set_display_area(cx - px * width2,
768                      cy - py * height2,
769                      cx + (1 - px) * width2,
770                      cy + (1 - py) * height2,
771                      0.0);
774 /**
775  * Zoom to center with absolute zoom factor.
776  */
777 void
778 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
780     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
783 /**
784  * Zoom to point with relative zoom factor.
785  */
786 void
787 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
789     NR::Rect const area = get_display_area();
791     if (cx < area.min()[NR::X]) {
792         cx = area.min()[NR::X];
793     }
794     if (cx > area.max()[NR::X]) {
795         cx = area.max()[NR::X];
796     }
797     if (cy < area.min()[NR::Y]) {
798         cy = area.min()[NR::Y];
799     }
800     if (cy > area.max()[NR::Y]) {
801         cy = area.max()[NR::Y];
802     }
804     gdouble const scale = expansion(_d2w) * zoom;
805     double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
806     double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
808     zoom_absolute_keep_point(cx, cy, px, py, scale);
811 /**
812  * Zoom to center with relative zoom factor.
813  */
814 void
815 SPDesktop::zoom_relative (double cx, double cy, double zoom)
817     gdouble scale = expansion(_d2w) * zoom;
818     zoom_absolute (cx, cy, scale);
821 /**
822  * Set display area to origin and current document dimensions.
823  */
824 void
825 SPDesktop::zoom_page()
827     NR::Rect d(NR::Point(0, 0),
828                NR::Point(sp_document_width(doc()), sp_document_height(doc())));
830     if (d.isEmpty(1.0)) {
831         return;
832     }
834     set_display_area(d, 10);
837 /**
838  * Set display area to current document width.
839  */
840 void
841 SPDesktop::zoom_page_width()
843     NR::Rect const a = get_display_area();
845     if (sp_document_width(doc()) < 1.0) {
846         return;
847     }
849     NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
850                NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
852     set_display_area(d, 10);
855 /**
856  * Zoom to selection.
857  */
858 void
859 SPDesktop::zoom_selection()
861     NR::Maybe<NR::Rect> const d = selection->bounds();
863     if ( !d || d->isEmpty(0.1) ) {
864         return;
865     }
867     set_display_area(*d, 10);
870 /**
871  * Tell widget to let zoom widget grab keyboard focus.
872  */
873 void
874 SPDesktop::zoom_grab_focus()
876     _widget->letZoomGrabFocus();
879 /**
880  * Zoom to whole drawing.
881  */
882 void
883 SPDesktop::zoom_drawing()
885     g_return_if_fail (doc() != NULL);
886     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
887     g_return_if_fail (docitem != NULL);
889     NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
891     /* Note that the second condition here indicates that
892     ** there are no items in the drawing.
893     */
894     if ( !d || d->isEmpty(1.0) ) {
895         return;
896     }
898     set_display_area(*d, 10);
901 /**
902  * Scroll canvas by specific coordinate amount.
903  */
904 void
905 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
907     g_assert(_widget);
909     NR::Rect const viewbox = canvas->getViewbox();
911     sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
913     _widget->updateRulers();
914     _widget->updateScrollbars(expansion(_d2w));
917 bool
918 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
920     gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
922     // autoscrolldistance is in screen pixels, but the display area is in document units
923     autoscrolldistance /= expansion(_d2w);
924     NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
926     if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
927         !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y])   ) {
929         NR::Point const s_w( (*p) * _d2w );
931         gdouble x_to;
932         if ((*p)[NR::X] < dbox.min()[NR::X])
933             x_to = dbox.min()[NR::X];
934         else if ((*p)[NR::X] > dbox.max()[NR::X])
935             x_to = dbox.max()[NR::X];
936         else
937             x_to = (*p)[NR::X];
939         gdouble y_to;
940         if ((*p)[NR::Y] < dbox.min()[NR::Y])
941             y_to = dbox.min()[NR::Y];
942         else if ((*p)[NR::Y] > dbox.max()[NR::Y])
943             y_to = dbox.max()[NR::Y];
944         else
945             y_to = (*p)[NR::Y];
947         NR::Point const d_dt(x_to, y_to);
948         NR::Point const d_w( d_dt * _d2w );
949         NR::Point const moved_w( d_w - s_w );
951         if (autoscrollspeed == 0)
952             autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
954         if (autoscrollspeed != 0)
955             scroll_world (autoscrollspeed * moved_w);
957         return true;
958     }
959     return false;
962 void
963 SPDesktop::fullscreen()
965     _widget->setFullscreen();
968 void
969 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
971     _widget->getGeometry (x, y, w, h);
974 void
975 SPDesktop::setWindowPosition (NR::Point p)
977     _widget->setPosition (p);
980 void
981 SPDesktop::setWindowSize (gint w, gint h)
983     _widget->setSize (w, h);
986 void
987 SPDesktop::setWindowTransient (void *p, int transient_policy)
989     _widget->setTransient (p, transient_policy);
992 void
993 SPDesktop::presentWindow()
995     _widget->present();
998 bool
999 SPDesktop::warnDialog (gchar *text)
1001     return _widget->warnDialog (text);
1004 void
1005 SPDesktop::toggleRulers()
1007     _widget->toggleRulers();
1010 void
1011 SPDesktop::toggleScrollbars()
1013     _widget->toggleScrollbars();
1016 void
1017 SPDesktop::layoutWidget()
1019     _widget->layout();
1022 void
1023 SPDesktop::destroyWidget()
1025     _widget->destroy();
1028 bool
1029 SPDesktop::shutdown()
1031     return _widget->shutdown();
1034 void
1035 SPDesktop::setToolboxFocusTo (gchar const *label)
1037     _widget->setToolboxFocusTo (label);
1040 void
1041 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1043     _widget->setToolboxAdjustmentValue (id, val);
1046 bool
1047 SPDesktop::isToolboxButtonActive (gchar const *id)
1049     return _widget->isToolboxButtonActive (id);
1052 void
1053 SPDesktop::emitToolSubselectionChanged(gpointer data)
1055         _tool_subselection_changed.emit(data);
1056         inkscape_subselection_changed (this);
1059 void
1060 SPDesktop::updateNow()
1062   sp_canvas_update_now(canvas);
1065 void
1066 SPDesktop::enableInteraction()
1068   _widget->enableInteraction();
1071 void SPDesktop::disableInteraction()
1073   _widget->disableInteraction();
1076 void SPDesktop::setWaitingCursor()
1078     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1079     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1080     gdk_cursor_unref(waiting);
1081     waiting_cursor = true;
1083     // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1084     // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1085     // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1086     // after the call to setWaitingCursor as it was before
1087     while( Gtk::Main::events_pending() )
1088        Gtk::Main::iteration();
1091 void SPDesktop::clearWaitingCursor()
1093   if (waiting_cursor)
1094       sp_event_context_update_cursor(sp_desktop_event_context(this));
1098 //----------------------------------------------------------------------
1099 // Callback implementations. The virtual ones are connected by the view.
1101 void
1102 SPDesktop::onPositionSet (double x, double y)
1104     _widget->viewSetPosition (NR::Point(x,y));
1107 void
1108 SPDesktop::onResized (double x, double y)
1110    // Nothing called here
1113 /**
1114  * Redraw callback; queues Gtk redraw; connected by View.
1115  */
1116 void
1117 SPDesktop::onRedrawRequested ()
1119     if (main) {
1120         _widget->requestCanvasUpdate();
1121     }
1124 void
1125 SPDesktop::updateCanvasNow()
1127   _widget->requestCanvasUpdateAndWait();
1130 /**
1131  * Associate document with desktop.
1132  */
1133 /// \todo fixme: refactor SPDesktop::init to use setDocument
1134 void
1135 SPDesktop::setDocument (SPDocument *doc)
1137     if (this->doc() && doc) {
1138         namedview->hide(this);
1139         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1140     }
1142     if (_layer_hierarchy) {
1143         _layer_hierarchy->clear();
1144         delete _layer_hierarchy;
1145     }
1146     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1147     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1148     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1149     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1150     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1152     /* setup EventLog */
1153     event_log = new Inkscape::EventLog(doc);
1154     doc->addUndoObserver(*event_log);
1156     _commit_connection.disconnect();
1157     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1159     /// \todo fixme: This condition exists to make sure the code
1160     /// inside is called only once on initialization. But there
1161     /// are surely more safe methods to accomplish this.
1162     if (drawing) {
1163         NRArenaItem *ai;
1165         namedview = sp_document_namedview (doc, NULL);
1166         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1167         number = namedview->getViewCount();
1169         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1170                 SP_CANVAS_ARENA (drawing)->arena,
1171                 dkey,
1172                 SP_ITEM_SHOW_DISPLAY);
1173         if (ai) {
1174             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1175             nr_arena_item_unref (ai);
1176         }
1177         namedview->show(this);
1178         /* Ugly hack */
1179         activate_guides (true);
1180         /* Ugly hack */
1181         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1182     }
1184     _document_replaced_signal.emit (this, doc);
1186     View::setDocument (doc);
1189 void
1190 SPDesktop::onStatusMessage
1191 (Inkscape::MessageType type, gchar const *message)
1193     if (_widget) {
1194         _widget->setMessage(type, message);
1195     }
1198 void
1199 SPDesktop::onDocumentURISet (gchar const* uri)
1201     _widget->setTitle(uri);
1204 /**
1205  * Resized callback.
1206  */
1207 void
1208 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1210     _doc2dt[5] = height;
1211     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1212     NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1213     SP_CTRLRECT(page)->setRectangle(a);
1214     SP_CTRLRECT(page_border)->setRectangle(a);
1218 void
1219 SPDesktop::_onActivate (SPDesktop* dt)
1221     if (!dt->_widget) return;
1222     dt->_widget->activateDesktop();
1225 void
1226 SPDesktop::_onDeactivate (SPDesktop* dt)
1228     if (!dt->_widget) return;
1229     dt->_widget->deactivateDesktop();
1232 void
1233 SPDesktop::_onSelectionModified
1234 (Inkscape::Selection *selection, guint flags, SPDesktop *dt)
1236     if (!dt->_widget) return;
1237     dt->_widget->updateScrollbars (expansion(dt->_d2w));
1240 static void
1241 _onSelectionChanged
1242 (Inkscape::Selection *selection, SPDesktop *desktop)
1244     /** \todo
1245      * only change the layer for single selections, or what?
1246      * This seems reasonable -- for multiple selections there can be many
1247      * different layers involved.
1248      */
1249     SPItem *item=selection->singleItem();
1250     if (item) {
1251         SPObject *layer=desktop->layerForObject(item);
1252         if ( layer && layer != desktop->currentLayer() ) {
1253             desktop->setCurrentLayer(layer);
1254         }
1255     }
1258 /**
1259  * Calls event handler of current event context.
1260  * \param arena Unused
1261  * \todo fixme
1262  */
1263 static gint
1264 _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1266     if (ai) {
1267         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1268         return sp_event_context_item_handler (desktop->event_context, spi, event);
1269     } else {
1270         return sp_event_context_root_handler (desktop->event_context, event);
1271     }
1274 static void
1275 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1276     g_return_if_fail(SP_IS_GROUP(layer));
1277     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1280 /// Callback
1281 static void
1282 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1283     g_return_if_fail(SP_IS_GROUP(layer));
1284     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1287 /// Callback
1288 static void
1289 _layer_hierarchy_changed(SPObject *top, SPObject *bottom,
1290                                          SPDesktop *desktop)
1292     desktop->_layer_changed_signal.emit (bottom);
1295 /// Called when document is starting to be rebuilt.
1296 static void
1297 _reconstruction_start (SPDesktop * desktop)
1299     // printf("Desktop, starting reconstruction\n");
1300     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1301     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1303     /*
1304     GSList const * selection_objs = desktop->selection->list();
1305     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1307     }
1308     */
1309     desktop->selection->clear();
1311     // printf("Desktop, starting reconstruction end\n");
1314 /// Called when document rebuild is finished.
1315 static void
1316 _reconstruction_finish (SPDesktop * desktop)
1318     // printf("Desktop, finishing reconstruction\n");
1319     if (desktop->_reconstruction_old_layer_id == NULL)
1320         return;
1322     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1323     if (newLayer != NULL)
1324         desktop->setCurrentLayer(newLayer);
1326     g_free(desktop->_reconstruction_old_layer_id);
1327     desktop->_reconstruction_old_layer_id = NULL;
1328     // printf("Desktop, finishing reconstruction end\n");
1329     return;
1332 /**
1333  * Namedview_modified callback.
1334  */
1335 static void
1336 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1338     SPNamedView *nv=SP_NAMEDVIEW(obj);
1340     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1342         /* Recalculate snap distances */
1343         /* FIXME: why is the desktop getting involved in setting up something
1344         ** that is entirely to do with the namedview?
1345         */
1346         _update_snap_distances (desktop);
1348         /* Show/hide page background */
1349         if (nv->pagecolor & 0xff) {
1350             sp_canvas_item_show (desktop->table);
1351             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1352             sp_canvas_item_move_to_z (desktop->table, 0);
1353         } else {
1354             sp_canvas_item_hide (desktop->table);
1355         }
1357         /* Show/hide page border */
1358         if (nv->showborder) {
1359             // show
1360             sp_canvas_item_show (desktop->page_border);
1361             // set color and shadow
1362             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1363             if (nv->pageshadow) {
1364                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1365             }
1366             // place in the z-order stack
1367             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1368                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1369             } else {
1370                 int order = sp_canvas_item_order (desktop->page_border);
1371                 int morder = sp_canvas_item_order (desktop->drawing);
1372                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1373                                     morder - order);
1374             }
1375         } else {
1376                 sp_canvas_item_hide (desktop->page_border);
1377                 if (nv->pageshadow) {
1378                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1379                 }
1380         }
1381         
1382         /* Show/hide page shadow */
1383         if (nv->showpageshadow && nv->pageshadow) {
1384             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1385         } else {
1386             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1387         }
1389         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1390             (SP_RGBA32_R_U(nv->pagecolor) +
1391              SP_RGBA32_G_U(nv->pagecolor) +
1392              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1393             // the background color is light or transparent, use black outline
1394             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1395         } else { // use white outline
1396             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1397         }
1398     }
1401 /**
1402  * Callback to reset snapper's distances.
1403  */
1404 static void
1405 _update_snap_distances (SPDesktop *desktop)
1407     SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1409     SPNamedView &nv = *desktop->namedview;
1411     
1412     // FIXME GRID: make one gridsnapper object that snaps to all enabled grids by calling their snappers.
1413     nv.snap_manager.grid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1414                                                                       *nv.gridtoleranceunit,
1415                                                                       px));
1416     nv.snap_manager.axonomgrid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1417                                                                       *nv.gridtoleranceunit,
1418                                                                       px));
1419     //new grid snappers
1420     for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1421         Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1422         grid->snapper->setDistance(sp_convert_distance_full(nv.gridtolerance,
1423                                                                       *nv.gridtoleranceunit,
1424                                                                       px));
1425     }
1426     
1427     nv.snap_manager.guide.setDistance(sp_convert_distance_full(nv.guidetolerance,
1428                                                                        *nv.guidetoleranceunit,
1429                                                                        px));
1430     nv.snap_manager.object.setDistance(sp_convert_distance_full(nv.objecttolerance,
1431                                                                         *nv.objecttoleranceunit,
1432                                                                         px));
1436 NR::Matrix SPDesktop::w2d() const
1438     return _w2d;
1441 NR::Point SPDesktop::w2d(NR::Point const &p) const
1443     return p * _w2d;
1446 NR::Point SPDesktop::d2w(NR::Point const &p) const
1448     return p * _d2w;
1451 NR::Matrix SPDesktop::doc2dt() const
1453     return _doc2dt;
1456 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1458     return p * _doc2dt;
1461 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1463     return p / _doc2dt;
1467 /**
1468  * Pop event context from desktop's context stack. Never used.
1469  */
1470 // void
1471 // SPDesktop::pop_event_context (unsigned int key)
1472 // {
1473 //    SPEventContext *ec = NULL;
1474 //
1475 //    if (event_context && event_context->key == key) {
1476 //        g_return_if_fail (event_context);
1477 //        g_return_if_fail (event_context->next);
1478 //        ec = event_context;
1479 //        sp_event_context_deactivate (ec);
1480 //        event_context = ec->next;
1481 //        sp_event_context_activate (event_context);
1482 //        _event_context_changed_signal.emit (this, ec);
1483 //    }
1484 //
1485 //    SPEventContext *ref = event_context;
1486 //    while (ref && ref->next && ref->next->key != key)
1487 //        ref = ref->next;
1488 //
1489 //    if (ref && ref->next) {
1490 //        ec = ref->next;
1491 //        ref->next = ec->next;
1492 //    }
1493 //
1494 //    if (ec) {
1495 //        sp_event_context_finish (ec);
1496 //        g_object_unref (G_OBJECT (ec));
1497 //    }
1498 // }
1500 /*
1501   Local Variables:
1502   mode:c++
1503   c-file-style:"stroustrup"
1504   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1505   indent-tabs-mode:nil
1506   fill-column:99
1507   End:
1508 */
1509 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :