Code

Updated overwrite confirmation dialog to be similar to the new stock GTK+ one
[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 SPDesktop::getToplevel( GtkWidget*& toplevel )
994     toplevel = GTK_WIDGET( _widget->getWindow() );
997 void
998 SPDesktop::presentWindow()
1000     _widget->present();
1003 bool
1004 SPDesktop::warnDialog (gchar *text)
1006     return _widget->warnDialog (text);
1009 void
1010 SPDesktop::toggleRulers()
1012     _widget->toggleRulers();
1015 void
1016 SPDesktop::toggleScrollbars()
1018     _widget->toggleScrollbars();
1021 void
1022 SPDesktop::layoutWidget()
1024     _widget->layout();
1027 void
1028 SPDesktop::destroyWidget()
1030     _widget->destroy();
1033 bool
1034 SPDesktop::shutdown()
1036     return _widget->shutdown();
1039 void
1040 SPDesktop::setToolboxFocusTo (gchar const *label)
1042     _widget->setToolboxFocusTo (label);
1045 void
1046 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1048     _widget->setToolboxAdjustmentValue (id, val);
1051 bool
1052 SPDesktop::isToolboxButtonActive (gchar const *id)
1054     return _widget->isToolboxButtonActive (id);
1057 void
1058 SPDesktop::emitToolSubselectionChanged(gpointer data)
1060         _tool_subselection_changed.emit(data);
1061         inkscape_subselection_changed (this);
1064 void
1065 SPDesktop::updateNow()
1067   sp_canvas_update_now(canvas);
1070 void
1071 SPDesktop::enableInteraction()
1073   _widget->enableInteraction();
1076 void SPDesktop::disableInteraction()
1078   _widget->disableInteraction();
1081 void SPDesktop::setWaitingCursor()
1083     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1084     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1085     gdk_cursor_unref(waiting);
1086     waiting_cursor = true;
1088     // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1089     // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1090     // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1091     // after the call to setWaitingCursor as it was before
1092     while( Gtk::Main::events_pending() )
1093        Gtk::Main::iteration();
1096 void SPDesktop::clearWaitingCursor()
1098   if (waiting_cursor)
1099       sp_event_context_update_cursor(sp_desktop_event_context(this));
1102 void SPDesktop::toggleGrid()
1104     for ( GSList const *l = namedview->grids; l != NULL; l = l->next) {
1105         Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1106         grid->toggle_visibility();
1107     }
1111 //----------------------------------------------------------------------
1112 // Callback implementations. The virtual ones are connected by the view.
1114 void
1115 SPDesktop::onPositionSet (double x, double y)
1117     _widget->viewSetPosition (NR::Point(x,y));
1120 void
1121 SPDesktop::onResized (double x, double y)
1123    // Nothing called here
1126 /**
1127  * Redraw callback; queues Gtk redraw; connected by View.
1128  */
1129 void
1130 SPDesktop::onRedrawRequested ()
1132     if (main) {
1133         _widget->requestCanvasUpdate();
1134     }
1137 void
1138 SPDesktop::updateCanvasNow()
1140   _widget->requestCanvasUpdateAndWait();
1143 /**
1144  * Associate document with desktop.
1145  */
1146 /// \todo fixme: refactor SPDesktop::init to use setDocument
1147 void
1148 SPDesktop::setDocument (SPDocument *doc)
1150     if (this->doc() && doc) {
1151         namedview->hide(this);
1152         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1153     }
1155     if (_layer_hierarchy) {
1156         _layer_hierarchy->clear();
1157         delete _layer_hierarchy;
1158     }
1159     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1160     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1161     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1162     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1163     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1165     /* setup EventLog */
1166     event_log = new Inkscape::EventLog(doc);
1167     doc->addUndoObserver(*event_log);
1169     _commit_connection.disconnect();
1170     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1172     /// \todo fixme: This condition exists to make sure the code
1173     /// inside is called only once on initialization. But there
1174     /// are surely more safe methods to accomplish this.
1175     if (drawing) {
1176         NRArenaItem *ai;
1178         namedview = sp_document_namedview (doc, NULL);
1179         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1180         number = namedview->getViewCount();
1182         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1183                 SP_CANVAS_ARENA (drawing)->arena,
1184                 dkey,
1185                 SP_ITEM_SHOW_DISPLAY);
1186         if (ai) {
1187             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1188             nr_arena_item_unref (ai);
1189         }
1190         namedview->show(this);
1191         /* Ugly hack */
1192         activate_guides (true);
1193         /* Ugly hack */
1194         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1195     }
1197     _document_replaced_signal.emit (this, doc);
1199     View::setDocument (doc);
1202 void
1203 SPDesktop::onStatusMessage
1204 (Inkscape::MessageType type, gchar const *message)
1206     if (_widget) {
1207         _widget->setMessage(type, message);
1208     }
1211 void
1212 SPDesktop::onDocumentURISet (gchar const* uri)
1214     _widget->setTitle(uri);
1217 /**
1218  * Resized callback.
1219  */
1220 void
1221 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1223     _doc2dt[5] = height;
1224     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1225     NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1226     SP_CTRLRECT(page)->setRectangle(a);
1227     SP_CTRLRECT(page_border)->setRectangle(a);
1231 void
1232 SPDesktop::_onActivate (SPDesktop* dt)
1234     if (!dt->_widget) return;
1235     dt->_widget->activateDesktop();
1238 void
1239 SPDesktop::_onDeactivate (SPDesktop* dt)
1241     if (!dt->_widget) return;
1242     dt->_widget->deactivateDesktop();
1245 void
1246 SPDesktop::_onSelectionModified
1247 (Inkscape::Selection *selection, guint flags, SPDesktop *dt)
1249     if (!dt->_widget) return;
1250     dt->_widget->updateScrollbars (expansion(dt->_d2w));
1253 static void
1254 _onSelectionChanged
1255 (Inkscape::Selection *selection, SPDesktop *desktop)
1257     /** \todo
1258      * only change the layer for single selections, or what?
1259      * This seems reasonable -- for multiple selections there can be many
1260      * different layers involved.
1261      */
1262     SPItem *item=selection->singleItem();
1263     if (item) {
1264         SPObject *layer=desktop->layerForObject(item);
1265         if ( layer && layer != desktop->currentLayer() ) {
1266             desktop->setCurrentLayer(layer);
1267         }
1268     }
1271 /**
1272  * Calls event handler of current event context.
1273  * \param arena Unused
1274  * \todo fixme
1275  */
1276 static gint
1277 _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1279     if (ai) {
1280         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1281         return sp_event_context_item_handler (desktop->event_context, spi, event);
1282     } else {
1283         return sp_event_context_root_handler (desktop->event_context, event);
1284     }
1287 static void
1288 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1289     g_return_if_fail(SP_IS_GROUP(layer));
1290     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1293 /// Callback
1294 static void
1295 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1296     g_return_if_fail(SP_IS_GROUP(layer));
1297     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1300 /// Callback
1301 static void
1302 _layer_hierarchy_changed(SPObject *top, SPObject *bottom,
1303                                          SPDesktop *desktop)
1305     desktop->_layer_changed_signal.emit (bottom);
1308 /// Called when document is starting to be rebuilt.
1309 static void
1310 _reconstruction_start (SPDesktop * desktop)
1312     // printf("Desktop, starting reconstruction\n");
1313     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1314     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1316     /*
1317     GSList const * selection_objs = desktop->selection->list();
1318     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1320     }
1321     */
1322     desktop->selection->clear();
1324     // printf("Desktop, starting reconstruction end\n");
1327 /// Called when document rebuild is finished.
1328 static void
1329 _reconstruction_finish (SPDesktop * desktop)
1331     // printf("Desktop, finishing reconstruction\n");
1332     if (desktop->_reconstruction_old_layer_id == NULL)
1333         return;
1335     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1336     if (newLayer != NULL)
1337         desktop->setCurrentLayer(newLayer);
1339     g_free(desktop->_reconstruction_old_layer_id);
1340     desktop->_reconstruction_old_layer_id = NULL;
1341     // printf("Desktop, finishing reconstruction end\n");
1342     return;
1345 /**
1346  * Namedview_modified callback.
1347  */
1348 static void
1349 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1351     SPNamedView *nv=SP_NAMEDVIEW(obj);
1353     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1355         /* Recalculate snap distances */
1356         /* FIXME: why is the desktop getting involved in setting up something
1357         ** that is entirely to do with the namedview?
1358         */
1359         _update_snap_distances (desktop);
1361         /* Show/hide page background */
1362         if (nv->pagecolor & 0xff) {
1363             sp_canvas_item_show (desktop->table);
1364             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1365             sp_canvas_item_move_to_z (desktop->table, 0);
1366         } else {
1367             sp_canvas_item_hide (desktop->table);
1368         }
1370         /* Show/hide page border */
1371         if (nv->showborder) {
1372             // show
1373             sp_canvas_item_show (desktop->page_border);
1374             // set color and shadow
1375             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1376             if (nv->pageshadow) {
1377                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1378             }
1379             // place in the z-order stack
1380             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1381                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1382             } else {
1383                 int order = sp_canvas_item_order (desktop->page_border);
1384                 int morder = sp_canvas_item_order (desktop->drawing);
1385                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1386                                     morder - order);
1387             }
1388         } else {
1389                 sp_canvas_item_hide (desktop->page_border);
1390                 if (nv->pageshadow) {
1391                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1392                 }
1393         }
1394         
1395         /* Show/hide page shadow */
1396         if (nv->showpageshadow && nv->pageshadow) {
1397             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1398         } else {
1399             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1400         }
1402         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1403             (SP_RGBA32_R_U(nv->pagecolor) +
1404              SP_RGBA32_G_U(nv->pagecolor) +
1405              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1406             // the background color is light or transparent, use black outline
1407             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1408         } else { // use white outline
1409             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1410         }
1411     }
1414 /**
1415  * Callback to reset snapper's distances.
1416  */
1417 static void
1418 _update_snap_distances (SPDesktop *desktop)
1420     SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1422     SPNamedView &nv = *desktop->namedview;
1424     
1425     // FIXME GRID: make one gridsnapper object that snaps to all enabled grids by calling their snappers.
1426     nv.snap_manager.grid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1427                                                                       *nv.gridtoleranceunit,
1428                                                                       px));
1429     //new grid snappers
1430     for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1431         Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1432         grid->snapper->setDistance(sp_convert_distance_full(nv.gridtolerance,
1433                                                                       *nv.gridtoleranceunit,
1434                                                                       px));
1435     }
1436     
1437     nv.snap_manager.guide.setDistance(sp_convert_distance_full(nv.guidetolerance,
1438                                                                        *nv.guidetoleranceunit,
1439                                                                        px));
1440     nv.snap_manager.object.setDistance(sp_convert_distance_full(nv.objecttolerance,
1441                                                                         *nv.objecttoleranceunit,
1442                                                                         px));
1446 NR::Matrix SPDesktop::w2d() const
1448     return _w2d;
1451 NR::Point SPDesktop::w2d(NR::Point const &p) const
1453     return p * _w2d;
1456 NR::Point SPDesktop::d2w(NR::Point const &p) const
1458     return p * _d2w;
1461 NR::Matrix SPDesktop::doc2dt() const
1463     return _doc2dt;
1466 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1468     return p * _doc2dt;
1471 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1473     return p / _doc2dt;
1477 /**
1478  * Pop event context from desktop's context stack. Never used.
1479  */
1480 // void
1481 // SPDesktop::pop_event_context (unsigned int key)
1482 // {
1483 //    SPEventContext *ec = NULL;
1484 //
1485 //    if (event_context && event_context->key == key) {
1486 //        g_return_if_fail (event_context);
1487 //        g_return_if_fail (event_context->next);
1488 //        ec = event_context;
1489 //        sp_event_context_deactivate (ec);
1490 //        event_context = ec->next;
1491 //        sp_event_context_activate (event_context);
1492 //        _event_context_changed_signal.emit (this, ec);
1493 //    }
1494 //
1495 //    SPEventContext *ref = event_context;
1496 //    while (ref && ref->next && ref->next->key != key)
1497 //        ref = ref->next;
1498 //
1499 //    if (ref && ref->next) {
1500 //        ec = ref->next;
1501 //        ref->next = ec->next;
1502 //    }
1503 //
1504 //    if (ec) {
1505 //        sp_event_context_finish (ec);
1506 //        g_object_unref (G_OBJECT (ec));
1507 //    }
1508 // }
1510 /*
1511   Local Variables:
1512   mode:c++
1513   c-file-style:"stroustrup"
1514   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1515   indent-tabs-mode:nil
1516   fill-column:99
1517   End:
1518 */
1519 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :