Code

- new: Print Colors Preview Dialog and rendermode
[inkscape.git] / src / desktop.cpp
index 7d99043421fb2272c7bdd05221481cf7c1206d2b..0e4d4caf3d1f4b7904a47c7196bf73f179794294 100644 (file)
@@ -8,7 +8,12 @@
  *   MenTaLguY <mental@rydia.net>
  *   bulia byak <buliabyak@users.sf.net>
  *   Ralf Stephan <ralf@ark.in-berlin.de>
+ *   John Bintz <jcoswell@coswellproductions.org>
+ *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
  *
+ * Copyright (C) 2007 Jon A. Cruz
+ * Copyright (C) 2006-2008 Johan Engelen
+ * Copyright (C) 2006 John Bintz
  * Copyright (C) 2004 MenTaLguY
  * Copyright (C) 1999-2002 Lauris Kaplinski
  * Copyright (C) 2000-2001 Ximian, Inc.
 #endif
 
 #include <glibmm/i18n.h>
+#include <sigc++/functors/mem_fun.h>
+#include <gtkmm.h>
 
+#include <2geom/rect.h>
 #include "macros.h"
 #include "inkscape-private.h"
 #include "desktop.h"
@@ -62,7 +70,7 @@
 #include "sp-namedview.h"
 #include "color.h"
 #include "sp-item-group.h"
-#include "prefs-utils.h"
+#include "preferences.h"
 #include "object-hierarchy.h"
 #include "helper/units.h"
 #include "display/canvas-arena.h"
 #include "display/gnome-canvas-acetate.h"
 #include "display/sodipodi-ctrlrect.h"
 #include "display/sp-canvas-util.h"
-#include "libnr/nr-matrix-div.h"
-#include "libnr/nr-rect-ops.h"
+#include "display/canvas-temporary-item-list.h"
+#include "display/snap-indicator.h"
 #include "ui/dialog/dialog-manager.h"
 #include "xml/repr.h"
 #include "message-context.h"
+#include "device-manager.h"
+#include "layer-fns.h"
 #include "layer-manager.h"
+#include "event-log.h"
+#include "display/canvas-grid.h"
+#include "widgets/desktop-widget.h"
+#include "box3d-context.h"
 
-#ifdef WITH_INKBOARD
-#include "jabber_whiteboard/session-manager.h"
-#endif
+#include "display/sp-canvas.h"
 
 namespace Inkscape { namespace XML { class Node; }}
 
@@ -91,58 +103,78 @@ static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
 static void _reconstruction_start(SPDesktop * desktop);
 static void _reconstruction_finish(SPDesktop * desktop);
-static void _namedview_modified (SPNamedView *nv, guint flags, SPDesktop *desktop);
-static void _update_snap_distances (SPDesktop *desktop);
+static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
 
 /**
  * Return new desktop object.
  * \pre namedview != NULL.
  * \pre canvas != NULL.
  */
-SPDesktop::SPDesktop()
-{
-    _dlg_mgr = NULL;
-    _widget = 0;
-    namedview = NULL;
-    selection = NULL;
-    acetate = NULL;
-    main = NULL;
-    grid = NULL;
-    guides = NULL;
-    drawing = NULL;
-    sketch = NULL;
-    controls = NULL;
-    event_context = 0;
-    layer_manager = 0;
-
-    _d2w.set_identity();
-    _w2d.set_identity();
-    _doc2dt = NR::Matrix(NR::scale(1, -1));
-
-    guides_active = false;
-
-    zooms_past = NULL;
-    zooms_future = NULL;
-
-    is_fullscreen = false;
-
-    gr_item = NULL;
-    gr_point_num = 0;
-    gr_fill_or_stroke = true;
-
-    _layer_hierarchy = NULL;
-    _active = false;
-
-    selection = Inkscape::GC::release (new Inkscape::Selection (this));
+SPDesktop::SPDesktop() :
+    _dlg_mgr( 0 ),
+    namedview( 0 ),
+    canvas( 0 ),
+    selection( 0 ),
+    event_context( 0 ),
+    layer_manager( 0 ),
+    event_log( 0 ),
+    temporary_item_list( 0 ),
+    snapindicator( 0 ),
+    acetate( 0 ),
+    main( 0 ),
+    gridgroup( 0 ),
+    guides( 0 ),
+    drawing( 0 ),
+    sketch( 0 ),
+    controls( 0 ),
+    tempgroup ( 0 ),
+    table( 0 ),
+    page( 0 ),
+    page_border( 0 ),
+    current( 0 ),
+    _focusMode(false),
+    zooms_past( 0 ),
+    zooms_future( 0 ),
+    dkey( 0 ),
+    number( 0 ),
+    window_state(0),
+    interaction_disabled_counter( 0 ),
+    waiting_cursor( false ),
+    guides_active( false ),
+    gr_item( 0 ),
+    gr_point_type( 0 ),
+    gr_point_i( 0 ),
+    gr_fill_or_stroke( true ),
+    _layer_hierarchy( 0 ),
+    _reconstruction_old_layer_id( 0 ),
+    _display_mode(Inkscape::RENDERMODE_NORMAL),
+    _widget( 0 ),
+    _inkscape( 0 ),
+    _guides_message_context( 0 ),
+    _active( false ),
+    _w2d(),
+    _d2w(),
+    _doc2dt( Geom::Scale(1, -1) ),
+    grids_visible( false )
+{
+    _d2w.setIdentity();
+    _w2d.setIdentity();
+
+    selection = Inkscape::GC::release( new Inkscape::Selection(this) );
 }
 
 void
-SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
-
+SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas, Inkscape::UI::View::EditWidgetInterface *widget)
 {
+    _widget = widget;
+
+    // Temporary workaround for link order issues:
+    Inkscape::DeviceManager::getManager().getDevices();
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+
     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
 
-    current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
+    current = prefs->getStyle("/desktop/style");
 
     namedview = nv;
     canvas = aCanvas;
@@ -152,7 +184,7 @@ SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
     sp_document_ensure_up_to_date (document);
 
     /* Setup Dialog Manager */
-    _dlg_mgr = new Inkscape::UI::Dialog::DialogManager();
+    _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
 
     dkey = sp_item_display_key_new (1);
 
@@ -174,7 +206,7 @@ SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
 
     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
-    SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
+    SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
     sp_canvas_item_move_to_z (table, 0);
 
@@ -185,15 +217,27 @@ SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
 
-    SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
+    SP_CANVAS_ARENA (drawing)->arena->delta = prefs->getDouble("/options/cursortolerance/value", 1.0); // default is 1 px
 
-    // Start always in normal mode
-    SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
-    canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
+    if (prefs->getBool("/options/startmode/outline")) {
+        // Start in outline mode
+        setDisplayModeOutline();
+    } else {
+        // Start in normal mode, default
+        setDisplayModeNormal();
+    }
 
-    grid = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
+    // The order in which these canvas items are added determines the z-order. It's therefore
+    // important to add the tempgroup (which will contain the snapindicator) before adding the
+    // controls. Only this way one will be able to quickly (before the snap indicator has
+    // disappeared) reselect a node after snapping it. If the z-order is wrong however, this
+    // will not work (the snap indicator is on top of the node handler; is the snapindicator
+    // being selected? or does it intercept some of the events that should have gone to the
+    // node handler? see bug https://bugs.launchpad.net/inkscape/+bug/414142)
+    gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
+    tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
 
     /* Push select tool to the bottom of stack */
@@ -202,12 +246,12 @@ SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
      * call "set" instead of "push".  Can we assume that there is only one
      * context ever?
      */
-    push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
+    push_event_context (SP_TYPE_SELECT_CONTEXT, "/tools/select", SP_EVENT_CONTEXT_STATIC);
 
     // display rect and zoom are now handled in sp_desktop_widget_realize()
 
-    NR::Rect const d(NR::Point(0.0, 0.0),
-                     NR::Point(sp_document_width(document), sp_document_height(document)));
+    Geom::Rect const d(Geom::Point(0.0, 0.0),
+                       Geom::Point(sp_document_width(document), sp_document_height(document)));
 
     SP_CTRLRECT(page)->setRectangle(d);
     SP_CTRLRECT(page_border)->setRectangle(d);
@@ -227,8 +271,7 @@ SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
     _doc2dt[5] = sp_document_height (document);
     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
 
-    g_signal_connect (G_OBJECT (namedview), "modified", G_CALLBACK (_namedview_modified), this);
-
+    _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
 
     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
             SP_CANVAS_ARENA (drawing)->arena,
@@ -236,7 +279,6 @@ SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
             SP_ITEM_SHOW_DISPLAY);
     if (ai) {
         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
-        nr_arena_item_unref (ai);
     }
 
     namedview->show(this);
@@ -245,14 +287,6 @@ SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
     /* Ugly hack */
     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
 
-       /* Construct SessionManager
-        *
-        * SessionManager construction needs to be done after document connection
-        */
-#ifdef WITH_INKBOARD
-       _whiteboard_session_manager = new Inkscape::Whiteboard::SessionManager(this);
-#endif
-
 /* Set up notification of rebuilding the document, this allows
        for saving object related settings in the document. */
     _reconstruction_start_connection =
@@ -295,15 +329,44 @@ SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
     /* setup LayerManager */
     //   (Setting up after the connections are all in place, as it may use some of them)
     layer_manager = new Inkscape::LayerManager( this );
+
+    showGrids(namedview->grids_visible, false);
+
+    temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
+    snapindicator = new Inkscape::Display::SnapIndicator ( this );
 }
 
 
 void SPDesktop::destroy()
 {
+    if (snapindicator) {
+        delete snapindicator;
+        snapindicator = NULL;
+    }
+    if (temporary_item_list) {
+        delete temporary_item_list;
+        temporary_item_list = NULL;
+    }
+
+    if (selection) {
+        delete selection;
+        selection = NULL;
+    }
+
+    namedview->hide(this);
+
     _activate_connection.disconnect();
     _deactivate_connection.disconnect();
     _sel_modified_connection.disconnect();
     _sel_changed_connection.disconnect();
+    _modified_connection.disconnect();
+    _commit_connection.disconnect();
+    _reconstruction_start_connection.disconnect();
+    _reconstruction_finish_connection.disconnect();
+
+    g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
+    g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
+    g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
 
     while (event_context) {
         SPEventContext *ec = event_context;
@@ -314,6 +377,12 @@ void SPDesktop::destroy()
 
     if (_layer_hierarchy) {
         delete _layer_hierarchy;
+//        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
+    }
+
+    if (layer_manager) {
+        delete layer_manager;
+        layer_manager = NULL;
     }
 
     if (_inkscape) {
@@ -325,17 +394,9 @@ void SPDesktop::destroy()
         drawing = NULL;
     }
 
-#ifdef WITH_INKBOARD
-       if (_whiteboard_session_manager) {
-               delete _whiteboard_session_manager;
-       }
-#endif
-
     delete _guides_message_context;
     _guides_message_context = NULL;
 
-    sp_signal_disconnect_by_data (G_OBJECT (namedview), this);
-
     g_list_free (zooms_past);
     g_list_free (zooms_future);
 }
@@ -345,18 +406,64 @@ SPDesktop::~SPDesktop() {}
 //--------------------------------------------------------------------
 /* Public methods */
 
-void SPDesktop::setDisplayModeNormal()
+
+/* These methods help for temporarily showing things on-canvas.
+ * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
+ * is when you want to prematurely remove the item from the canvas, by calling
+ * desktop->remove_temporary_canvasitem(tempitem).
+ */
+/** Note that lifetime is measured in milliseconds
+ * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
+ * delete the object for you and the reference will become invalid without you knowing it.
+ * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
+ * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
+ * because the object might be deleted already without you knowing it.
+ * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
+ */
+Inkscape::Display::TemporaryItem *
+SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
 {
-    SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
-    canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
-    sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
+    if (move_to_bottom) {
+        sp_canvas_item_move_to_z(item, 0);
+    }
+
+    return temporary_item_list->add_item(item, lifetime);
 }
 
-void SPDesktop::setDisplayModeOutline()
+/** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
+*/
+void
+SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
 {
-    SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
-    canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
+    // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
+    if (tempitem && temporary_item_list) {
+        temporary_item_list->delete_item(tempitem);
+    }
+}
+
+void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
+    SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
+    canvas->rendermode = mode;
+    _display_mode = mode;
     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
+    _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
+}
+
+void SPDesktop::displayModeToggle() {
+    switch (_display_mode) {
+    case Inkscape::RENDERMODE_NORMAL:
+        _setDisplayMode(Inkscape::RENDERMODE_NO_FILTERS);
+        break;
+    case Inkscape::RENDERMODE_NO_FILTERS:
+        _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
+        break;
+    case Inkscape::RENDERMODE_OUTLINE:
+        _setDisplayMode(Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW);
+        break;
+    case Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW:
+    default:
+        _setDisplayMode(Inkscape::RENDERMODE_NORMAL);
+    }
 }
 
 /**
@@ -377,7 +484,7 @@ SPObject *SPDesktop::currentLayer() const
 
 /**
  * Sets the current layer of the desktop.
- * 
+ *
  * Make \a object the top layer.
  */
 void SPDesktop::setCurrentLayer(SPObject *object) {
@@ -387,6 +494,31 @@ void SPDesktop::setCurrentLayer(SPObject *object) {
     _layer_hierarchy->setBottom(object);
 }
 
+void SPDesktop::toggleLayerSolo(SPObject *object) {
+    g_return_if_fail(SP_IS_GROUP(object));
+    g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
+
+    bool othersShowing = false;
+    std::vector<SPObject*> layers;
+    for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
+        layers.push_back(obj);
+        othersShowing |= !SP_ITEM(obj)->isHidden();
+    }
+    for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
+        layers.push_back(obj);
+        othersShowing |= !SP_ITEM(obj)->isHidden();
+    }
+
+
+    if ( SP_ITEM(object)->isHidden() ) {
+        SP_ITEM(object)->setHidden(false);
+    }
+
+    for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
+        SP_ITEM(*it)->setHidden(othersShowing);
+    }
+}
+
 /**
  * Return layer that contains \a object.
  */
@@ -415,9 +547,13 @@ bool SPDesktop::isLayer(SPObject *object) const {
  */
 bool SPDesktop::isWithinViewport (SPItem *item) const
 {
-    NR::Rect const viewport = get_display_area();
-    NR::Rect const bbox = sp_item_bbox_desktop(item);
-    return viewport.contains(bbox);
+    Geom::Rect const viewport = get_display_area();
+    Geom::OptRect const bbox = sp_item_bbox_desktop(item);
+    if (bbox) {
+        return viewport.contains(*bbox);
+    } else {
+        return true;
+    }
 }
 
 ///
@@ -463,6 +599,15 @@ SPDesktop::change_document (SPDocument *theDocument)
     selection->clear();
 
     setDocument (theDocument);
+
+    /* update the rulers, connect the desktop widget's signal to the new namedview etc.
+       (this can probably be done in a better way) */
+    Gtk::Window *parent = this->getToplevel();
+    g_assert(parent != NULL);
+    SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
+    if (dtw) dtw->desktop = this;
+    sp_desktop_widget_update_namedview(dtw);
+
     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
     _document_replaced_signal.emit (this, theDocument);
 }
@@ -482,8 +627,7 @@ SPDesktop::set_event_context (GtkType type, const gchar *config)
         g_object_unref (G_OBJECT (ec));
     }
 
-    Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
-    ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
+    ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
     ec->next = event_context;
     event_context = ec;
     sp_event_context_activate (ec);
@@ -497,7 +641,6 @@ void
 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
 {
     SPEventContext *ref, *ec;
-    Inkscape::XML::Node *repr;
 
     if (event_context && event_context->key == key) return;
     ref = event_context;
@@ -510,8 +653,7 @@ SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int k
     }
 
     if (event_context) sp_event_context_deactivate (event_context);
-    repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
-    ec = sp_event_context_new (type, this, repr, key);
+    ec = sp_event_context_new (type, this, config, key);
     ec->next = event_context;
     event_context = ec;
     sp_event_context_activate (ec);
@@ -522,7 +664,7 @@ SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int k
  * Sets the coordinate status to a given point
  */
 void
-SPDesktop::set_coordinate_status (NR::Point p) {
+SPDesktop::set_coordinate_status (Geom::Point p) {
     _widget->setCoordinateStatus(p);
 }
 
@@ -530,7 +672,7 @@ SPDesktop::set_coordinate_status (NR::Point p) {
  * \see sp_document_item_from_list_at_point_bottom()
  */
 SPItem *
-SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
+SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
 {
     g_return_val_if_fail (doc() != NULL, NULL);
     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
@@ -540,7 +682,7 @@ SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p
  * \see sp_document_item_at_point()
  */
 SPItem *
-SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
+SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
 {
     g_return_val_if_fail (doc() != NULL, NULL);
     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
@@ -550,7 +692,7 @@ SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) con
  * \see sp_document_group_at_point()
  */
 SPItem *
-SPDesktop::group_at_point (NR::Point const p) const
+SPDesktop::group_at_point (Geom::Point const p) const
 {
     g_return_val_if_fail (doc() != NULL, NULL);
     return sp_document_group_at_point (doc(), dkey, p);
@@ -560,22 +702,22 @@ SPDesktop::group_at_point (NR::Point const p) const
  * \brief  Returns the mouse point in document coordinates; if mouse is
  * outside the canvas, returns the center of canvas viewpoint
  */
-NR::Point
+Geom::Point
 SPDesktop::point() const
 {
-    NR::Point p = _widget->getPointer();
-    NR::Point pw = sp_canvas_window_to_world (canvas, p);
+    Geom::Point p = _widget->getPointer();
+    Geom::Point pw = sp_canvas_window_to_world (canvas, p);
     p = w2d(pw);
 
-    NR::Rect const r = canvas->getViewbox();
+    Geom::Rect const r = canvas->getViewbox();
 
-    NR::Point r0 = w2d(r.min());
-    NR::Point r1 = w2d(r.max());
+    Geom::Point r0 = w2d(r.min());
+    Geom::Point r1 = w2d(r.max());
 
-    if (p[NR::X] >= r0[NR::X] &&
-        p[NR::X] <= r1[NR::X] &&
-        p[NR::Y] >= r1[NR::Y] &&
-        p[NR::Y] <= r0[NR::Y])
+    if (p[Geom::X] >= r0[Geom::X] &&
+        p[Geom::X] <= r1[Geom::X] &&
+        p[Geom::Y] >= r1[Geom::Y] &&
+        p[Geom::Y] <= r0[Geom::Y])
     {
         return p;
     } else {
@@ -589,13 +731,13 @@ SPDesktop::point() const
 void
 SPDesktop::push_current_zoom (GList **history)
 {
-    NR::Rect const area = get_display_area();
+    Geom::Rect const area = get_display_area();
 
     NRRect *old_zoom = g_new(NRRect, 1);
-    old_zoom->x0 = area.min()[NR::X];
-    old_zoom->x1 = area.max()[NR::X];
-    old_zoom->y0 = area.min()[NR::Y];
-    old_zoom->y1 = area.max()[NR::Y];
+    old_zoom->x0 = area.min()[Geom::X];
+    old_zoom->x1 = area.max()[Geom::X];
+    old_zoom->y0 = area.min()[Geom::Y];
+    old_zoom->y1 = area.max()[Geom::Y];
     if ( *history == NULL
          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
@@ -607,7 +749,7 @@ SPDesktop::push_current_zoom (GList **history)
 }
 
 /**
- * Set viewbox.
+ * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
  */
 void
 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
@@ -625,55 +767,60 @@ SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double
     double const cx = 0.5 * (x0 + x1);
     double const cy = 0.5 * (y0 + y1);
 
-    NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
+    // FIXME: This 2geom idiom doesn't allow us to declare dbox const
+    Geom::Rect viewbox = canvas->getViewbox();
+    viewbox.expandBy(-border);
 
-    double scale = expansion(_d2w);
+    double scale = _d2w.descrim();
     double newscale;
-    if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
-        newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
+    if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
+        newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
     } else {
-        newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
+        newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
     }
 
-    newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
+    newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
 
     int clear = FALSE;
     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
         /* Set zoom factors */
-        _d2w = NR::Matrix(NR::scale(newscale, -newscale));
-        _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
+        _d2w = Geom::Scale(newscale, -newscale);
+        _w2d = Geom::Scale(1/newscale, 1/-newscale);
         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
         clear = TRUE;
     }
 
-    /* Calculate top left corner */
-    x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
-    y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
+    /* Calculate top left corner (in document pixels) */
+    x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
+    y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
 
     /* Scroll */
     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
 
+    /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
+    sp_box3d_context_update_lines(event_context);
+
     _widget->updateRulers();
-    _widget->updateScrollbars(expansion(_d2w));
+    _widget->updateScrollbars(_d2w.descrim());
     _widget->updateZoom();
 }
 
-void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
+void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
 {
-    set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
+    set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
 }
 
 /**
  * Return viewbox dimensions.
  */
-NR::Rect SPDesktop::get_display_area() const
+Geom::Rect SPDesktop::get_display_area() const
 {
-    NR::Rect const viewbox = canvas->getViewbox();
+    Geom::Rect const viewbox = canvas->getViewbox();
 
     double const scale = _d2w[0];
 
-    return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
-                    NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
+    return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
+                      Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
 }
 
 /**
@@ -726,6 +873,103 @@ SPDesktop::next_zoom()
     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
 }
 
+#include "tools-switch.h"
+#include "node-context.h"
+#include "shape-editor.h"
+#include "nodepath.h"
+
+/** \brief  Performs a quick zoom into what the user is working on
+    \param  enable  Whether we're going in or out of quick zoom
+
+*/
+void
+SPDesktop::zoom_quick (bool enable)
+{
+    if (enable == _quick_zoom_enabled) {
+        return;
+    }
+
+    if (enable == true) {
+        _quick_zoom_stored_area = get_display_area();
+        bool zoomed = false;
+
+        if (!zoomed) {
+            SPItem * singleItem = selection->singleItem();
+            if (singleItem != NULL && tools_isactive(this, TOOLS_NODES)) {
+
+                Inkscape::NodePath::Path * nodepath = event_context->shape_editor->get_nodepath();
+                // printf("I've got a nodepath, crazy\n");
+
+                               if (nodepath) {
+                                       Geom::Rect nodes;
+                                       bool firstnode = true;
+
+                                       if (nodepath->selected) {
+                                               for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
+                                                  Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
+                                                       for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
+                                                          Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
+                                                               if (node->selected) {
+                                                                       // printf("\tSelected node\n");
+                                                                       if (firstnode) {
+                                                                               nodes = Geom::Rect(node->pos, node->pos);
+                                                                               firstnode = false;
+                                                                       } else {
+                                                                               nodes.expandTo(node->pos);
+                                                                       }
+
+                                                                       if (node->p.other != NULL) {
+                                                                               /* Include previous node pos */
+                                                                               nodes.expandTo(node->p.other->pos);
+
+                                                                               /* Include previous handle */
+                                                                               if (!sp_node_side_is_line(node, &node->p)) {
+                                                                                       nodes.expandTo(node->p.pos);
+                                                                               }
+                                                                       }
+
+                                                                       if (node->n.other != NULL) {
+                                                                               /* Include previous node pos */
+                                                                               nodes.expandTo(node->n.other->pos);
+
+                                                                               /* Include previous handle */
+                                                                               if (!sp_node_side_is_line(node, &node->n)) {
+                                                                                       nodes.expandTo(node->n.pos);
+                                                                               }
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+
+                                               if (!firstnode && nodes.area() * 2.0 < _quick_zoom_stored_area.area()) {
+                                                       set_display_area(nodes, 10);
+                                                       zoomed = true;
+                                               }
+                                       }
+                               }
+            }
+        }
+
+        if (!zoomed) {
+            Geom::OptRect const d = selection->bounds();
+            if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
+                set_display_area(*d, 10);
+                zoomed = true;
+            }
+        }
+
+        if (!zoomed) {
+            zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
+            zoomed = true;
+        }
+    } else {
+        set_display_area(_quick_zoom_stored_area, 0);
+    }
+
+    _quick_zoom_enabled = enable;
+    return;
+}
+
 /**
  * Zoom to point with absolute zoom factor.
  */
@@ -737,13 +981,13 @@ SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py,
     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
     // this check prevents "sliding" when trying to zoom in at maximum zoom;
     /// \todo someone please fix calculations properly and remove this hack
-    if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
+    if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
         return;
 
-    NR::Rect const viewbox = canvas->getViewbox();
+    Geom::Rect const viewbox = canvas->getViewbox();
 
-    double const width2 = viewbox.dimensions()[NR::X] / zoom;
-    double const height2 = viewbox.dimensions()[NR::Y] / zoom;
+    double const width2 = viewbox.dimensions()[Geom::X] / zoom;
+    double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
 
     set_display_area(cx - px * width2,
                      cy - py * height2,
@@ -767,24 +1011,24 @@ SPDesktop::zoom_absolute (double cx, double cy, double zoom)
 void
 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
 {
-    NR::Rect const area = get_display_area();
+    Geom::Rect const area = get_display_area();
 
-    if (cx < area.min()[NR::X]) {
-        cx = area.min()[NR::X];
+    if (cx < area.min()[Geom::X]) {
+        cx = area.min()[Geom::X];
     }
-    if (cx > area.max()[NR::X]) {
-        cx = area.max()[NR::X];
+    if (cx > area.max()[Geom::X]) {
+        cx = area.max()[Geom::X];
     }
-    if (cy < area.min()[NR::Y]) {
-        cy = area.min()[NR::Y];
+    if (cy < area.min()[Geom::Y]) {
+        cy = area.min()[Geom::Y];
     }
-    if (cy > area.max()[NR::Y]) {
-        cy = area.max()[NR::Y];
+    if (cy > area.max()[Geom::Y]) {
+        cy = area.max()[Geom::Y];
     }
 
-    gdouble const scale = expansion(_d2w) * zoom;
-    double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
-    double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
+    gdouble const scale = _d2w.descrim() * zoom;
+    double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
+    double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
 
     zoom_absolute_keep_point(cx, cy, px, py, scale);
 }
@@ -795,7 +1039,7 @@ SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
 void
 SPDesktop::zoom_relative (double cx, double cy, double zoom)
 {
-    gdouble scale = expansion(_d2w) * zoom;
+    gdouble scale = _d2w.descrim() * zoom;
     zoom_absolute (cx, cy, scale);
 }
 
@@ -805,10 +1049,10 @@ SPDesktop::zoom_relative (double cx, double cy, double zoom)
 void
 SPDesktop::zoom_page()
 {
-    NR::Rect d(NR::Point(0, 0),
-               NR::Point(sp_document_width(doc()), sp_document_height(doc())));
+    Geom::Rect d(Geom::Point(0, 0),
+                 Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
 
-    if (d.dimensions()[NR::X] < 1.0 || d.dimensions()[NR::Y] < 1.0) {
+    if (d.minExtent() < 1.0) {
         return;
     }
 
@@ -821,14 +1065,14 @@ SPDesktop::zoom_page()
 void
 SPDesktop::zoom_page_width()
 {
-    NR::Rect const a = get_display_area();
+    Geom::Rect const a = get_display_area();
 
     if (sp_document_width(doc()) < 1.0) {
         return;
     }
 
-    NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
-               NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
+    Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
+                 Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
 
     set_display_area(d, 10);
 }
@@ -839,13 +1083,13 @@ SPDesktop::zoom_page_width()
 void
 SPDesktop::zoom_selection()
 {
-    NR::Rect const d = selection->bounds();
+    Geom::OptRect const d = selection->bounds();
 
-    if (d.dimensions()[NR::X] < 0.1 || d.dimensions()[NR::Y] < 0.1) {
+    if ( !d || d->minExtent() < 0.1 ) {
         return;
     }
 
-    set_display_area(d, 10);
+    set_display_area(*d, 10);
 }
 
 /**
@@ -867,70 +1111,89 @@ SPDesktop::zoom_drawing()
     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
     g_return_if_fail (docitem != NULL);
 
-    NR::Rect d = sp_item_bbox_desktop(docitem);
+    Geom::OptRect d = sp_item_bbox_desktop(docitem);
 
     /* Note that the second condition here indicates that
     ** there are no items in the drawing.
     */
-    if ( d.dimensions()[NR::X] < 1.0 || d.dimensions()[NR::Y] < 1.0 ) {
+    if ( !d || d->minExtent() < 0.1 ) {
         return;
     }
 
-    set_display_area(d, 10);
+    set_display_area(*d, 10);
+}
+
+/**
+ * Scroll canvas by specific coordinate amount in svg coordinates.
+ */
+void
+SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
+{
+    double scale = _d2w.descrim();
+    scroll_world(dx*scale, dy*scale, is_scrolling);
 }
 
 /**
  * Scroll canvas by specific coordinate amount.
  */
 void
-SPDesktop::scroll_world (double dx, double dy)
+SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
 {
     g_assert(_widget);
 
-    NR::Rect const viewbox = canvas->getViewbox();
+    Geom::Rect const viewbox = canvas->getViewbox();
+
+    sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
 
-    sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE);
+    /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
+    sp_box3d_context_update_lines(event_context);
 
     _widget->updateRulers();
-    _widget->updateScrollbars(expansion(_d2w));
+    _widget->updateScrollbars(_d2w.descrim());
 }
 
 bool
-SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
+SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
 {
-    gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
+    using Geom::X;
+    using Geom::Y;
+
+    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+    gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
 
     // autoscrolldistance is in screen pixels, but the display area is in document units
-    autoscrolldistance /= expansion(_d2w);
-    NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
+    autoscrolldistance /= _d2w.descrim();
+    // FIXME: This 2geom idiom doesn't allow us to declare dbox const
+    Geom::Rect dbox = get_display_area();
+    dbox.expandBy(-autoscrolldistance);
 
-    if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
-        !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y])   ) {
+    if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
+        !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y])   ) {
 
-        NR::Point const s_w( (*p) * _d2w );
+        Geom::Point const s_w( p * (Geom::Matrix)_d2w );
 
         gdouble x_to;
-        if ((*p)[NR::X] < dbox.min()[NR::X])
-            x_to = dbox.min()[NR::X];
-        else if ((*p)[NR::X] > dbox.max()[NR::X])
-            x_to = dbox.max()[NR::X];
+        if (p[X] < dbox.min()[X])
+            x_to = dbox.min()[X];
+        else if (p[X] > dbox.max()[X])
+            x_to = dbox.max()[X];
         else
-            x_to = (*p)[NR::X];
+            x_to = p[X];
 
         gdouble y_to;
-        if ((*p)[NR::Y] < dbox.min()[NR::Y])
-            y_to = dbox.min()[NR::Y];
-        else if ((*p)[NR::Y] > dbox.max()[NR::Y])
-            y_to = dbox.max()[NR::Y];
+        if (p[Y] < dbox.min()[Y])
+            y_to = dbox.min()[Y];
+        else if (p[Y] > dbox.max()[Y])
+            y_to = dbox.max()[Y];
         else
-            y_to = (*p)[NR::Y];
+            y_to = p[Y];
 
-        NR::Point const d_dt(x_to, y_to);
-        NR::Point const d_w( d_dt * _d2w );
-        NR::Point const moved_w( d_w - s_w );
+        Geom::Point const d_dt(x_to, y_to);
+        Geom::Point const d_w( d_dt * _d2w );
+        Geom::Point const moved_w( d_w - s_w );
 
         if (autoscrollspeed == 0)
-            autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
+            autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
 
         if (autoscrollspeed != 0)
             scroll_world (autoscrollspeed * moved_w);
@@ -940,12 +1203,69 @@ SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
     return false;
 }
 
+bool
+SPDesktop::is_iconified()
+{
+    return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
+}
+
+void
+SPDesktop::iconify()
+{
+    _widget->setIconified();
+}
+
+bool
+SPDesktop::is_maximized()
+{
+    return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
+}
+
+void
+SPDesktop::maximize()
+{
+    _widget->setMaximized();
+}
+
+bool
+SPDesktop::is_fullscreen()
+{
+    return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
+}
+
 void
 SPDesktop::fullscreen()
 {
     _widget->setFullscreen();
 }
 
+/** \brief  Checks to see if the user is working in focused mode
+
+    Returns the value of \c _focusMode
+*/
+bool
+SPDesktop::is_focusMode()
+{
+    return _focusMode;
+}
+
+/** \brief  Changes whether the user is in focus mode or not
+    \param  mode  Which mode the view should be in
+
+*/
+void
+SPDesktop::focusMode (bool mode)
+{
+    if (mode == _focusMode) { return; }
+
+    _focusMode = mode;
+
+    layoutWidget();
+    //sp_desktop_widget_layout(SPDesktopWidget);
+
+    return;
+}
+
 void
 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
 {
@@ -953,7 +1273,7 @@ SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
 }
 
 void
-SPDesktop::setWindowPosition (NR::Point p)
+SPDesktop::setWindowPosition (Geom::Point p)
 {
     _widget->setPosition (p);
 }
@@ -970,6 +1290,12 @@ SPDesktop::setWindowTransient (void *p, int transient_policy)
     _widget->setTransient (p, transient_policy);
 }
 
+Gtk::Window*
+SPDesktop::getToplevel( )
+{
+    return _widget->getWindow();
+}
+
 void
 SPDesktop::presentWindow()
 {
@@ -1012,6 +1338,37 @@ SPDesktop::shutdown()
     return _widget->shutdown();
 }
 
+bool SPDesktop::onDeleteUI (GdkEventAny*)
+{
+    if(shutdown())
+        return true;
+
+    destroyWidget();
+    return false;
+}
+
+/**
+ *  onWindowStateEvent
+ *
+ *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
+ *  Since GTK doesn't have a way to query this state information directly, we
+ *  record it for the desktop here, and also possibly trigger a layout.
+ */
+bool
+SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
+{
+    // Record the desktop window's state
+    window_state = event->new_window_state;
+
+    // Layout may differ depending on full-screen mode or not
+    GdkWindowState changed = event->changed_mask;
+    if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
+        layoutWidget();
+    }
+
+    return false;
+}
+
 void
 SPDesktop::setToolboxFocusTo (gchar const *label)
 {
@@ -1024,6 +1381,12 @@ SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
     _widget->setToolboxAdjustmentValue (id, val);
 }
 
+void
+SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
+{
+    _widget->setToolboxSelectOneValue (id, val);
+}
+
 bool
 SPDesktop::isToolboxButtonActive (gchar const *id)
 {
@@ -1033,8 +1396,78 @@ SPDesktop::isToolboxButtonActive (gchar const *id)
 void
 SPDesktop::emitToolSubselectionChanged(gpointer data)
 {
-       _tool_subselection_changed.emit(data);
-       inkscape_subselection_changed (this);
+    _tool_subselection_changed.emit(data);
+    inkscape_subselection_changed (this);
+}
+
+void
+SPDesktop::updateNow()
+{
+  sp_canvas_update_now(canvas);
+}
+
+void
+SPDesktop::enableInteraction()
+{
+  _widget->enableInteraction();
+}
+
+void SPDesktop::disableInteraction()
+{
+  _widget->disableInteraction();
+}
+
+void SPDesktop::setWaitingCursor()
+{
+    GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
+    gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
+    gdk_cursor_unref(waiting);
+    // GDK needs the flush for the cursor change to take effect
+    gdk_flush();
+    waiting_cursor = true;
+}
+
+void SPDesktop::clearWaitingCursor()
+{
+  if (waiting_cursor)
+      sp_event_context_update_cursor(sp_desktop_event_context(this));
+}
+
+void SPDesktop::toggleColorProfAdjust()
+{
+    _widget->toggleColorProfAdjust();
+}
+
+void SPDesktop::toggleGrids()
+{
+    if (namedview->grids) {
+        if(gridgroup) {
+            showGrids(!grids_visible);
+        }
+    } else {
+        //there is no grid present at the moment. add a rectangular grid and make it visible
+        Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
+        Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
+        showGrids(true);
+    }
+}
+
+void SPDesktop::showGrids(bool show, bool dirty_document)
+{
+    grids_visible = show;
+    sp_namedview_show_grids(namedview, grids_visible, dirty_document);
+    if (show) {
+        sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
+    } else {
+        sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
+    }
+}
+
+void SPDesktop::toggleSnapGlobal()
+{
+    bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
+    Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
+    sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
 }
 
 //----------------------------------------------------------------------
@@ -1043,11 +1476,11 @@ SPDesktop::emitToolSubselectionChanged(gpointer data)
 void
 SPDesktop::onPositionSet (double x, double y)
 {
-    _widget->viewSetPosition (NR::Point(x,y));
+    _widget->viewSetPosition (Geom::Point(x,y));
 }
 
 void
-SPDesktop::onResized (double x, double y)
+SPDesktop::onResized (double /*x*/, double /*y*/)
 {
    // Nothing called here
 }
@@ -1063,6 +1496,12 @@ SPDesktop::onRedrawRequested ()
     }
 }
 
+void
+SPDesktop::updateCanvasNow()
+{
+  _widget->requestCanvasUpdateAndWait();
+}
+
 /**
  * Associate document with desktop.
  */
@@ -1084,14 +1523,22 @@ SPDesktop::setDocument (SPDocument *doc)
     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
 
+    /* setup EventLog */
+    event_log = new Inkscape::EventLog(doc);
+    doc->addUndoObserver(*event_log);
+
+    _commit_connection.disconnect();
+    _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
+
     /// \todo fixme: This condition exists to make sure the code
-    /// inside is called only once on initialization. But there
+    /// inside is NOT called on initialization, only on replacement. But there
     /// are surely more safe methods to accomplish this.
+    // TODO since the comment had reversed logic, check the intent of this block of code:
     if (drawing) {
-        NRArenaItem *ai;
+        NRArenaItem *ai = 0;
 
         namedview = sp_document_namedview (doc, NULL);
-        g_signal_connect (G_OBJECT (namedview), "modified", G_CALLBACK (_namedview_modified), this);
+        _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
         number = namedview->getViewCount();
 
         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
@@ -1100,7 +1547,6 @@ SPDesktop::setDocument (SPDocument *doc)
                 SP_ITEM_SHOW_DISPLAY);
         if (ai) {
             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
-            nr_arena_item_unref (ai);
         }
         namedview->show(this);
         /* Ugly hack */
@@ -1137,7 +1583,7 @@ SPDesktop::onDocumentResized (gdouble width, gdouble height)
 {
     _doc2dt[5] = height;
     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
-    NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
+    Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
     SP_CTRLRECT(page)->setRectangle(a);
     SP_CTRLRECT(page_border)->setRectangle(a);
 }
@@ -1159,10 +1605,10 @@ SPDesktop::_onDeactivate (SPDesktop* dt)
 
 void
 SPDesktop::_onSelectionModified
-(Inkscape::Selection *selection, guint flags, SPDesktop *dt)
+(Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
 {
     if (!dt->_widget) return;
-    dt->_widget->updateScrollbars (expansion(dt->_d2w));
+    dt->_widget->updateScrollbars (dt->_d2w.descrim());
 }
 
 static void
@@ -1189,7 +1635,7 @@ _onSelectionChanged
  * \todo fixme
  */
 static gint
-_arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
+_arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
 {
     if (ai) {
         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
@@ -1214,7 +1660,7 @@ _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
 
 /// Callback
 static void
-_layer_hierarchy_changed(SPObject *top, SPObject *bottom,
+_layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
                                          SPDesktop *desktop)
 {
     desktop->_layer_changed_signal.emit (bottom);
@@ -1261,15 +1707,11 @@ _reconstruction_finish (SPDesktop * desktop)
  * Namedview_modified callback.
  */
 static void
-_namedview_modified (SPNamedView *nv, guint flags, SPDesktop *desktop)
+_namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
 {
-    if (flags & SP_OBJECT_MODIFIED_FLAG) {
+    SPNamedView *nv=SP_NAMEDVIEW(obj);
 
-        /* Recalculate snap distances */
-        /* FIXME: why is the desktop getting involved in setting up something
-        ** that is entirely to do with the namedview?
-        */
-        _update_snap_distances (desktop);
+    if (flags & SP_OBJECT_MODIFIED_FLAG) {
 
         /* Show/hide page background */
         if (nv->pagecolor & 0xff) {
@@ -1296,7 +1738,7 @@ _namedview_modified (SPNamedView *nv, guint flags, SPDesktop *desktop)
                 int order = sp_canvas_item_order (desktop->page_border);
                 int morder = sp_canvas_item_order (desktop->drawing);
                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
-                                   morder - order);
+                    morder - order);
             }
         } else {
                 sp_canvas_item_hide (desktop->page_border);
@@ -1304,7 +1746,7 @@ _namedview_modified (SPNamedView *nv, guint flags, SPDesktop *desktop)
                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
                 }
         }
-       
+
         /* Show/hide page shadow */
         if (nv->showpageshadow && nv->pageshadow) {
             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
@@ -1312,68 +1754,53 @@ _namedview_modified (SPNamedView *nv, guint flags, SPDesktop *desktop)
             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
         }
 
+        Inkscape::Preferences *prefs = Inkscape::Preferences::get();
         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
             (SP_RGBA32_R_U(nv->pagecolor) +
              SP_RGBA32_G_U(nv->pagecolor) +
              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
             // the background color is light or transparent, use black outline
-            SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = 0xff;
+            SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
         } else { // use white outline
-            SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = 0xffffffff;
+            SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
         }
     }
 }
 
-/**
- * Callback to reset snapper's distances.
- */
-static void
-_update_snap_distances (SPDesktop *desktop)
-{
-    SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
-
-    SPNamedView &nv = *desktop->namedview;
-
-    nv.snap_manager.grid.setDistance(sp_convert_distance_full(nv.gridtolerance,
-                                                                      *nv.gridtoleranceunit,
-                                                                      px));
-    nv.snap_manager.guide.setDistance(sp_convert_distance_full(nv.guidetolerance,
-                                                                       *nv.guidetoleranceunit,
-                                                                       px));
-    nv.snap_manager.object.setDistance(sp_convert_distance_full(nv.objecttolerance,
-                                                                        *nv.objecttoleranceunit,
-                                                                        px));
-}
-
-
-NR::Matrix SPDesktop::w2d() const
+Geom::Matrix SPDesktop::w2d() const
 {
     return _w2d;
 }
 
-NR::Point SPDesktop::w2d(NR::Point const &p) const
+Geom::Point SPDesktop::w2d(Geom::Point const &p) const
 {
     return p * _w2d;
 }
 
-NR::Point SPDesktop::d2w(NR::Point const &p) const
+Geom::Point SPDesktop::d2w(Geom::Point const &p) const
 {
     return p * _d2w;
 }
 
-NR::Matrix SPDesktop::doc2dt() const
+Geom::Matrix SPDesktop::doc2dt() const
+{
+    return _doc2dt;
+}
+
+Geom::Matrix SPDesktop::dt2doc() const
 {
+    // doc2dt is its own inverse
     return _doc2dt;
 }
 
-NR::Point SPDesktop::doc2dt(NR::Point const &p) const
+Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
 {
     return p * _doc2dt;
 }
 
-NR::Point SPDesktop::dt2doc(NR::Point const &p) const
+Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
 {
-    return p / _doc2dt;
+    return p * dt2doc();
 }