Code

New Grids are now ready to go. bug testing can start i think.
[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 #include "display/sp-canvas.h"
90 namespace Inkscape { namespace XML { class Node; }}
92 // Callback declarations
93 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
94 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
95 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
96 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
97 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
98 static void _reconstruction_start(SPDesktop * desktop);
99 static void _reconstruction_finish(SPDesktop * desktop);
100 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
101 static void _update_snap_distances (SPDesktop *desktop);
103 /**
104  * Return new desktop object.
105  * \pre namedview != NULL.
106  * \pre canvas != NULL.
107  */
108 SPDesktop::SPDesktop()
110     _dlg_mgr = NULL;
111     _widget = 0;
112     namedview = NULL;
113     selection = NULL;
114     acetate = NULL;
115     main = NULL;
116     gridgroup = NULL;
117     guides = NULL;
118     drawing = NULL;
119     sketch = NULL;
120     controls = NULL;
121     event_context = 0;
122     layer_manager = 0;
124     _d2w.set_identity();
125     _w2d.set_identity();
126     _doc2dt = NR::Matrix(NR::scale(1, -1));
128     guides_active = false;
130     zooms_past = NULL;
131     zooms_future = NULL;
133     is_fullscreen = false;
134     waiting_cursor = false;
136     gr_item = NULL;
137     gr_point_type = 0;
138     gr_point_i = 0;
139     gr_fill_or_stroke = true;
141     _layer_hierarchy = NULL;
142     _active = false;
144     selection = Inkscape::GC::release (new Inkscape::Selection (this));
147 void
148 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
151     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
153     current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
155     namedview = nv;
156     canvas = aCanvas;
158     SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
159     /* Kill flicker */
160     sp_document_ensure_up_to_date (document);
162     /* Setup Dialog Manager */
163     _dlg_mgr = new Inkscape::UI::Dialog::DialogManager();
165     dkey = sp_item_display_key_new (1);
167     /* Connect document */
168     setDocument (document);
170     number = namedview->getViewCount();
173     /* Setup Canvas */
174     g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
176     SPCanvasGroup *root = sp_canvas_root (canvas);
178     /* Setup adminstrative layers */
179     acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
180     g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
181     main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
182     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
184     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
185     SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
186     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
187     sp_canvas_item_move_to_z (table, 0);
189     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
190     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
191     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
193     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
194     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
196     SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
198     if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
199         // Start in outline mode
200         setDisplayModeOutline();
201     } else {
202         // Start in normal mode, default
203         setDisplayModeNormal();
204     }
206     gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
207     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
208     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
209     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
211     /* Push select tool to the bottom of stack */
212     /** \todo
213      * FIXME: this is the only call to this.  Everything else seems to just
214      * call "set" instead of "push".  Can we assume that there is only one
215      * context ever?
216      */
217     push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
219     // display rect and zoom are now handled in sp_desktop_widget_realize()
221     NR::Rect const d(NR::Point(0.0, 0.0),
222                      NR::Point(sp_document_width(document), sp_document_height(document)));
224     SP_CTRLRECT(page)->setRectangle(d);
225     SP_CTRLRECT(page_border)->setRectangle(d);
227     /* the following sets the page shadow on the canvas
228        It was originally set to 5, which is really cheesy!
229        It now is an attribute in the document's namedview. If a value of
230        0 is used, then the constructor for a shadow is not initialized.
231     */
233     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
234         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
235     }
238     /* Connect event for page resize */
239     _doc2dt[5] = sp_document_height (document);
240     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
242     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
244     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
245             SP_CANVAS_ARENA (drawing)->arena,
246             dkey,
247             SP_ITEM_SHOW_DISPLAY);
248     if (ai) {
249         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
250         nr_arena_item_unref (ai);
251     }
253     namedview->show(this);
254     /* Ugly hack */
255     activate_guides (true);
256     /* Ugly hack */
257     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
259 /* Set up notification of rebuilding the document, this allows
260        for saving object related settings in the document. */
261     _reconstruction_start_connection =
262         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
263     _reconstruction_finish_connection =
264         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
265     _reconstruction_old_layer_id = NULL;
266     
267     _commit_connection = document->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
268     
269     // ?
270     // sp_active_desktop_set (desktop);
271     _inkscape = INKSCAPE;
273     _activate_connection = _activate_signal.connect(
274         sigc::bind(
275             sigc::ptr_fun(_onActivate),
276             this
277         )
278     );
279      _deactivate_connection = _deactivate_signal.connect(
280         sigc::bind(
281             sigc::ptr_fun(_onDeactivate),
282             this
283         )
284     );
286     _sel_modified_connection = selection->connectModified(
287         sigc::bind(
288             sigc::ptr_fun(&_onSelectionModified),
289             this
290         )
291     );
292     _sel_changed_connection = selection->connectChanged(
293         sigc::bind(
294             sigc::ptr_fun(&_onSelectionChanged),
295             this
296         )
297     );
300     /* setup LayerManager */
301     //   (Setting up after the connections are all in place, as it may use some of them)
302     layer_manager = new Inkscape::LayerManager( this );
303     
304     grids_visible = true;
308 void SPDesktop::destroy()
310     _activate_connection.disconnect();
311     _deactivate_connection.disconnect();
312     _sel_modified_connection.disconnect();
313     _sel_changed_connection.disconnect();
314     _modified_connection.disconnect();
315     _commit_connection.disconnect();
316     _reconstruction_start_connection.disconnect();
317     _reconstruction_finish_connection.disconnect();
319     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
320     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
321     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
323     while (event_context) {
324         SPEventContext *ec = event_context;
325         event_context = ec->next;
326         sp_event_context_finish (ec);
327         g_object_unref (G_OBJECT (ec));
328     }
330     if (_layer_hierarchy) {
331         delete _layer_hierarchy;
332     }
334     if (_inkscape) {
335         _inkscape = NULL;
336     }
338     if (drawing) {
339         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
340         drawing = NULL;
341     }
343     delete _guides_message_context;
344     _guides_message_context = NULL;
346     g_list_free (zooms_past);
347     g_list_free (zooms_future);
350 SPDesktop::~SPDesktop() {}
352 //--------------------------------------------------------------------
353 /* Public methods */
355 void SPDesktop::setDisplayModeNormal()
357     SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
358     canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
359     displayMode = RENDERMODE_NORMAL;
360     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
361     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
364 void SPDesktop::setDisplayModeOutline()
366     SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
367     canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
368     displayMode = RENDERMODE_OUTLINE;
369     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
370     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
373 void SPDesktop::displayModeToggle()
375     if (displayMode == RENDERMODE_OUTLINE)
376         setDisplayModeNormal();
377     else 
378         setDisplayModeOutline();
381 /**
382  * Returns current root (=bottom) layer.
383  */
384 SPObject *SPDesktop::currentRoot() const
386     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
389 /**
390  * Returns current top layer.
391  */
392 SPObject *SPDesktop::currentLayer() const
394     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
397 /**
398  * Sets the current layer of the desktop.
399  * 
400  * Make \a object the top layer.
401  */
402 void SPDesktop::setCurrentLayer(SPObject *object) {
403     g_return_if_fail(SP_IS_GROUP(object));
404     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
405     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
406     _layer_hierarchy->setBottom(object);
409 /**
410  * Return layer that contains \a object.
411  */
412 SPObject *SPDesktop::layerForObject(SPObject *object) {
413     g_return_val_if_fail(object != NULL, NULL);
415     SPObject *root=currentRoot();
416     object = SP_OBJECT_PARENT(object);
417     while ( object && object != root && !isLayer(object) ) {
418         object = SP_OBJECT_PARENT(object);
419     }
420     return object;
423 /**
424  * True if object is a layer.
425  */
426 bool SPDesktop::isLayer(SPObject *object) const {
427     return ( SP_IS_GROUP(object)
428              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
429                   == SPGroup::LAYER ) );
432 /**
433  * True if desktop viewport fully contains \a item's bbox.
434  */
435 bool SPDesktop::isWithinViewport (SPItem *item) const
437     NR::Rect const viewport = get_display_area();
438     NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
439     if (bbox) {
440         return viewport.contains(*bbox);
441     } else {
442         return true;
443     }
446 ///
447 bool SPDesktop::itemIsHidden(SPItem const *item) const {
448     return item->isHidden(this->dkey);
451 /**
452  * Set activate property of desktop; emit signal if changed.
453  */
454 void
455 SPDesktop::set_active (bool new_active)
457     if (new_active != _active) {
458         _active = new_active;
459         if (new_active) {
460             _activate_signal.emit();
461         } else {
462             _deactivate_signal.emit();
463         }
464     }
467 /**
468  * Set activate status of current desktop's named view.
469  */
470 void
471 SPDesktop::activate_guides(bool activate)
473     guides_active = activate;
474     namedview->activateGuides(this, activate);
477 /**
478  * Make desktop switch documents.
479  */
480 void
481 SPDesktop::change_document (SPDocument *theDocument)
483     g_return_if_fail (theDocument != NULL);
485     /* unselect everything before switching documents */
486     selection->clear();
488     setDocument (theDocument);
489     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
490     _document_replaced_signal.emit (this, theDocument);
493 /**
494  * Make desktop switch event contexts.
495  */
496 void
497 SPDesktop::set_event_context (GtkType type, const gchar *config)
499     SPEventContext *ec;
500     while (event_context) {
501         ec = event_context;
502         sp_event_context_deactivate (ec);
503         event_context = ec->next;
504         sp_event_context_finish (ec);
505         g_object_unref (G_OBJECT (ec));
506     }
508     Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
509     ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
510     ec->next = event_context;
511     event_context = ec;
512     sp_event_context_activate (ec);
513     _event_context_changed_signal.emit (this, ec);
516 /**
517  * Push event context onto desktop's context stack.
518  */
519 void
520 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
522     SPEventContext *ref, *ec;
523     Inkscape::XML::Node *repr;
525     if (event_context && event_context->key == key) return;
526     ref = event_context;
527     while (ref && ref->next && ref->next->key != key) ref = ref->next;
528     if (ref && ref->next) {
529         ec = ref->next;
530         ref->next = ec->next;
531         sp_event_context_finish (ec);
532         g_object_unref (G_OBJECT (ec));
533     }
535     if (event_context) sp_event_context_deactivate (event_context);
536     repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
537     ec = sp_event_context_new (type, this, repr, key);
538     ec->next = event_context;
539     event_context = ec;
540     sp_event_context_activate (ec);
541     _event_context_changed_signal.emit (this, ec);
544 /**
545  * Sets the coordinate status to a given point
546  */
547 void
548 SPDesktop::set_coordinate_status (NR::Point p) {
549     _widget->setCoordinateStatus(p);
552 /**
553  * \see sp_document_item_from_list_at_point_bottom()
554  */
555 SPItem *
556 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
558     g_return_val_if_fail (doc() != NULL, NULL);
559     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
562 /**
563  * \see sp_document_item_at_point()
564  */
565 SPItem *
566 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
568     g_return_val_if_fail (doc() != NULL, NULL);
569     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
572 /**
573  * \see sp_document_group_at_point()
574  */
575 SPItem *
576 SPDesktop::group_at_point (NR::Point const p) const
578     g_return_val_if_fail (doc() != NULL, NULL);
579     return sp_document_group_at_point (doc(), dkey, p);
582 /**
583  * \brief  Returns the mouse point in document coordinates; if mouse is
584  * outside the canvas, returns the center of canvas viewpoint
585  */
586 NR::Point
587 SPDesktop::point() const
589     NR::Point p = _widget->getPointer();
590     NR::Point pw = sp_canvas_window_to_world (canvas, p);
591     p = w2d(pw);
593     NR::Rect const r = canvas->getViewbox();
595     NR::Point r0 = w2d(r.min());
596     NR::Point r1 = w2d(r.max());
598     if (p[NR::X] >= r0[NR::X] &&
599         p[NR::X] <= r1[NR::X] &&
600         p[NR::Y] >= r1[NR::Y] &&
601         p[NR::Y] <= r0[NR::Y])
602     {
603         return p;
604     } else {
605         return (r0 + r1) / 2;
606     }
609 /**
610  * Put current zoom data in history list.
611  */
612 void
613 SPDesktop::push_current_zoom (GList **history)
615     NR::Rect const area = get_display_area();
617     NRRect *old_zoom = g_new(NRRect, 1);
618     old_zoom->x0 = area.min()[NR::X];
619     old_zoom->x1 = area.max()[NR::X];
620     old_zoom->y0 = area.min()[NR::Y];
621     old_zoom->y1 = area.max()[NR::Y];
622     if ( *history == NULL
623          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
624                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
625                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
626                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
627     {
628         *history = g_list_prepend (*history, old_zoom);
629     }
632 /**
633  * Set viewbox.
634  */
635 void
636 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
638     g_assert(_widget);
640     // save the zoom
641     if (log) {
642         push_current_zoom(&zooms_past);
643         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
644         g_list_free (zooms_future);
645         zooms_future = NULL;
646     }
648     double const cx = 0.5 * (x0 + x1);
649     double const cy = 0.5 * (y0 + y1);
651     NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
653     double scale = expansion(_d2w);
654     double newscale;
655     if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
656         newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
657     } else {
658         newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
659     }
661     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
663     int clear = FALSE;
664     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
665         /* Set zoom factors */
666         _d2w = NR::Matrix(NR::scale(newscale, -newscale));
667         _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
668         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
669         clear = TRUE;
670     }
672     /* Calculate top left corner */
673     x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
674     y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
676     /* Scroll */
677     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
679     _widget->updateRulers();
680     _widget->updateScrollbars(expansion(_d2w));
681     _widget->updateZoom();
684 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
686     set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
689 /**
690  * Return viewbox dimensions.
691  */
692 NR::Rect SPDesktop::get_display_area() const
694     NR::Rect const viewbox = canvas->getViewbox();
696     double const scale = _d2w[0];
698     return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
699                     NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
702 /**
703  * Revert back to previous zoom if possible.
704  */
705 void
706 SPDesktop::prev_zoom()
708     if (zooms_past == NULL) {
709         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
710         return;
711     }
713     // push current zoom into forward zooms list
714     push_current_zoom (&zooms_future);
716     // restore previous zoom
717     set_display_area (((NRRect *) zooms_past->data)->x0,
718             ((NRRect *) zooms_past->data)->y0,
719             ((NRRect *) zooms_past->data)->x1,
720             ((NRRect *) zooms_past->data)->y1,
721             0, false);
723     // remove the just-added zoom from the past zooms list
724     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
727 /**
728  * Set zoom to next in list.
729  */
730 void
731 SPDesktop::next_zoom()
733     if (zooms_future == NULL) {
734         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
735         return;
736     }
738     // push current zoom into past zooms list
739     push_current_zoom (&zooms_past);
741     // restore next zoom
742     set_display_area (((NRRect *) zooms_future->data)->x0,
743             ((NRRect *) zooms_future->data)->y0,
744             ((NRRect *) zooms_future->data)->x1,
745             ((NRRect *) zooms_future->data)->y1,
746             0, false);
748     // remove the just-used zoom from the zooms_future list
749     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
752 /**
753  * Zoom to point with absolute zoom factor.
754  */
755 void
756 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
758     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
760     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
761     // this check prevents "sliding" when trying to zoom in at maximum zoom;
762     /// \todo someone please fix calculations properly and remove this hack
763     if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
764         return;
766     NR::Rect const viewbox = canvas->getViewbox();
768     double const width2 = viewbox.dimensions()[NR::X] / zoom;
769     double const height2 = viewbox.dimensions()[NR::Y] / zoom;
771     set_display_area(cx - px * width2,
772                      cy - py * height2,
773                      cx + (1 - px) * width2,
774                      cy + (1 - py) * height2,
775                      0.0);
778 /**
779  * Zoom to center with absolute zoom factor.
780  */
781 void
782 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
784     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
787 /**
788  * Zoom to point with relative zoom factor.
789  */
790 void
791 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
793     NR::Rect const area = get_display_area();
795     if (cx < area.min()[NR::X]) {
796         cx = area.min()[NR::X];
797     }
798     if (cx > area.max()[NR::X]) {
799         cx = area.max()[NR::X];
800     }
801     if (cy < area.min()[NR::Y]) {
802         cy = area.min()[NR::Y];
803     }
804     if (cy > area.max()[NR::Y]) {
805         cy = area.max()[NR::Y];
806     }
808     gdouble const scale = expansion(_d2w) * zoom;
809     double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
810     double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
812     zoom_absolute_keep_point(cx, cy, px, py, scale);
815 /**
816  * Zoom to center with relative zoom factor.
817  */
818 void
819 SPDesktop::zoom_relative (double cx, double cy, double zoom)
821     gdouble scale = expansion(_d2w) * zoom;
822     zoom_absolute (cx, cy, scale);
825 /**
826  * Set display area to origin and current document dimensions.
827  */
828 void
829 SPDesktop::zoom_page()
831     NR::Rect d(NR::Point(0, 0),
832                NR::Point(sp_document_width(doc()), sp_document_height(doc())));
834     if (d.isEmpty(1.0)) {
835         return;
836     }
838     set_display_area(d, 10);
841 /**
842  * Set display area to current document width.
843  */
844 void
845 SPDesktop::zoom_page_width()
847     NR::Rect const a = get_display_area();
849     if (sp_document_width(doc()) < 1.0) {
850         return;
851     }
853     NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
854                NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
856     set_display_area(d, 10);
859 /**
860  * Zoom to selection.
861  */
862 void
863 SPDesktop::zoom_selection()
865     NR::Maybe<NR::Rect> const d = selection->bounds();
867     if ( !d || d->isEmpty(0.1) ) {
868         return;
869     }
871     set_display_area(*d, 10);
874 /**
875  * Tell widget to let zoom widget grab keyboard focus.
876  */
877 void
878 SPDesktop::zoom_grab_focus()
880     _widget->letZoomGrabFocus();
883 /**
884  * Zoom to whole drawing.
885  */
886 void
887 SPDesktop::zoom_drawing()
889     g_return_if_fail (doc() != NULL);
890     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
891     g_return_if_fail (docitem != NULL);
893     NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
895     /* Note that the second condition here indicates that
896     ** there are no items in the drawing.
897     */
898     if ( !d || d->isEmpty(1.0) ) {
899         return;
900     }
902     set_display_area(*d, 10);
905 /**
906  * Scroll canvas by specific coordinate amount.
907  */
908 void
909 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
911     g_assert(_widget);
913     NR::Rect const viewbox = canvas->getViewbox();
915     sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
917     _widget->updateRulers();
918     _widget->updateScrollbars(expansion(_d2w));
921 bool
922 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
924     gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
926     // autoscrolldistance is in screen pixels, but the display area is in document units
927     autoscrolldistance /= expansion(_d2w);
928     NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
930     if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
931         !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y])   ) {
933         NR::Point const s_w( (*p) * _d2w );
935         gdouble x_to;
936         if ((*p)[NR::X] < dbox.min()[NR::X])
937             x_to = dbox.min()[NR::X];
938         else if ((*p)[NR::X] > dbox.max()[NR::X])
939             x_to = dbox.max()[NR::X];
940         else
941             x_to = (*p)[NR::X];
943         gdouble y_to;
944         if ((*p)[NR::Y] < dbox.min()[NR::Y])
945             y_to = dbox.min()[NR::Y];
946         else if ((*p)[NR::Y] > dbox.max()[NR::Y])
947             y_to = dbox.max()[NR::Y];
948         else
949             y_to = (*p)[NR::Y];
951         NR::Point const d_dt(x_to, y_to);
952         NR::Point const d_w( d_dt * _d2w );
953         NR::Point const moved_w( d_w - s_w );
955         if (autoscrollspeed == 0)
956             autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
958         if (autoscrollspeed != 0)
959             scroll_world (autoscrollspeed * moved_w);
961         return true;
962     }
963     return false;
966 void
967 SPDesktop::fullscreen()
969     _widget->setFullscreen();
972 void
973 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
975     _widget->getGeometry (x, y, w, h);
978 void
979 SPDesktop::setWindowPosition (NR::Point p)
981     _widget->setPosition (p);
984 void
985 SPDesktop::setWindowSize (gint w, gint h)
987     _widget->setSize (w, h);
990 void
991 SPDesktop::setWindowTransient (void *p, int transient_policy)
993     _widget->setTransient (p, transient_policy);
996 void SPDesktop::getToplevel( GtkWidget*& toplevel )
998     toplevel = GTK_WIDGET( _widget->getWindow() );
1001 void
1002 SPDesktop::presentWindow()
1004     _widget->present();
1007 bool
1008 SPDesktop::warnDialog (gchar *text)
1010     return _widget->warnDialog (text);
1013 void
1014 SPDesktop::toggleRulers()
1016     _widget->toggleRulers();
1019 void
1020 SPDesktop::toggleScrollbars()
1022     _widget->toggleScrollbars();
1025 void
1026 SPDesktop::layoutWidget()
1028     _widget->layout();
1031 void
1032 SPDesktop::destroyWidget()
1034     _widget->destroy();
1037 bool
1038 SPDesktop::shutdown()
1040     return _widget->shutdown();
1043 void
1044 SPDesktop::setToolboxFocusTo (gchar const *label)
1046     _widget->setToolboxFocusTo (label);
1049 void
1050 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1052     _widget->setToolboxAdjustmentValue (id, val);
1055 bool
1056 SPDesktop::isToolboxButtonActive (gchar const *id)
1058     return _widget->isToolboxButtonActive (id);
1061 void
1062 SPDesktop::emitToolSubselectionChanged(gpointer data)
1064         _tool_subselection_changed.emit(data);
1065         inkscape_subselection_changed (this);
1068 void
1069 SPDesktop::updateNow()
1071   sp_canvas_update_now(canvas);
1074 void
1075 SPDesktop::enableInteraction()
1077   _widget->enableInteraction();
1080 void SPDesktop::disableInteraction()
1082   _widget->disableInteraction();
1085 void SPDesktop::setWaitingCursor()
1087     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1088     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1089     gdk_cursor_unref(waiting);
1090     waiting_cursor = true;
1092     // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1093     // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1094     // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1095     // after the call to setWaitingCursor as it was before
1096     while( Gtk::Main::events_pending() )
1097        Gtk::Main::iteration();
1100 void SPDesktop::clearWaitingCursor()
1102   if (waiting_cursor)
1103       sp_event_context_update_cursor(sp_desktop_event_context(this));
1106 void SPDesktop::toggleGrid()
1108     if(gridgroup) {
1109         grids_visible = !grids_visible;
1110         if (grids_visible) {
1111             sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1112         } else {
1113             sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1114         }
1115     }
1119 //----------------------------------------------------------------------
1120 // Callback implementations. The virtual ones are connected by the view.
1122 void
1123 SPDesktop::onPositionSet (double x, double y)
1125     _widget->viewSetPosition (NR::Point(x,y));
1128 void
1129 SPDesktop::onResized (double x, double y)
1131    // Nothing called here
1134 /**
1135  * Redraw callback; queues Gtk redraw; connected by View.
1136  */
1137 void
1138 SPDesktop::onRedrawRequested ()
1140     if (main) {
1141         _widget->requestCanvasUpdate();
1142     }
1145 void
1146 SPDesktop::updateCanvasNow()
1148   _widget->requestCanvasUpdateAndWait();
1151 /**
1152  * Associate document with desktop.
1153  */
1154 /// \todo fixme: refactor SPDesktop::init to use setDocument
1155 void
1156 SPDesktop::setDocument (SPDocument *doc)
1158     if (this->doc() && doc) {
1159         namedview->hide(this);
1160         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1161     }
1163     if (_layer_hierarchy) {
1164         _layer_hierarchy->clear();
1165         delete _layer_hierarchy;
1166     }
1167     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1168     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1169     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1170     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1171     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1173     /* setup EventLog */
1174     event_log = new Inkscape::EventLog(doc);
1175     doc->addUndoObserver(*event_log);
1177     _commit_connection.disconnect();
1178     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1180     /// \todo fixme: This condition exists to make sure the code
1181     /// inside is called only once on initialization. But there
1182     /// are surely more safe methods to accomplish this.
1183     if (drawing) {
1184         NRArenaItem *ai;
1186         namedview = sp_document_namedview (doc, NULL);
1187         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1188         number = namedview->getViewCount();
1190         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1191                 SP_CANVAS_ARENA (drawing)->arena,
1192                 dkey,
1193                 SP_ITEM_SHOW_DISPLAY);
1194         if (ai) {
1195             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1196             nr_arena_item_unref (ai);
1197         }
1198         namedview->show(this);
1199         /* Ugly hack */
1200         activate_guides (true);
1201         /* Ugly hack */
1202         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1203     }
1205     _document_replaced_signal.emit (this, doc);
1207     View::setDocument (doc);
1210 void
1211 SPDesktop::onStatusMessage
1212 (Inkscape::MessageType type, gchar const *message)
1214     if (_widget) {
1215         _widget->setMessage(type, message);
1216     }
1219 void
1220 SPDesktop::onDocumentURISet (gchar const* uri)
1222     _widget->setTitle(uri);
1225 /**
1226  * Resized callback.
1227  */
1228 void
1229 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1231     _doc2dt[5] = height;
1232     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1233     NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1234     SP_CTRLRECT(page)->setRectangle(a);
1235     SP_CTRLRECT(page_border)->setRectangle(a);
1239 void
1240 SPDesktop::_onActivate (SPDesktop* dt)
1242     if (!dt->_widget) return;
1243     dt->_widget->activateDesktop();
1246 void
1247 SPDesktop::_onDeactivate (SPDesktop* dt)
1249     if (!dt->_widget) return;
1250     dt->_widget->deactivateDesktop();
1253 void
1254 SPDesktop::_onSelectionModified
1255 (Inkscape::Selection *selection, guint flags, SPDesktop *dt)
1257     if (!dt->_widget) return;
1258     dt->_widget->updateScrollbars (expansion(dt->_d2w));
1261 static void
1262 _onSelectionChanged
1263 (Inkscape::Selection *selection, SPDesktop *desktop)
1265     /** \todo
1266      * only change the layer for single selections, or what?
1267      * This seems reasonable -- for multiple selections there can be many
1268      * different layers involved.
1269      */
1270     SPItem *item=selection->singleItem();
1271     if (item) {
1272         SPObject *layer=desktop->layerForObject(item);
1273         if ( layer && layer != desktop->currentLayer() ) {
1274             desktop->setCurrentLayer(layer);
1275         }
1276     }
1279 /**
1280  * Calls event handler of current event context.
1281  * \param arena Unused
1282  * \todo fixme
1283  */
1284 static gint
1285 _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1287     if (ai) {
1288         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1289         return sp_event_context_item_handler (desktop->event_context, spi, event);
1290     } else {
1291         return sp_event_context_root_handler (desktop->event_context, event);
1292     }
1295 static void
1296 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1297     g_return_if_fail(SP_IS_GROUP(layer));
1298     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1301 /// Callback
1302 static void
1303 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1304     g_return_if_fail(SP_IS_GROUP(layer));
1305     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1308 /// Callback
1309 static void
1310 _layer_hierarchy_changed(SPObject *top, SPObject *bottom,
1311                                          SPDesktop *desktop)
1313     desktop->_layer_changed_signal.emit (bottom);
1316 /// Called when document is starting to be rebuilt.
1317 static void
1318 _reconstruction_start (SPDesktop * desktop)
1320     // printf("Desktop, starting reconstruction\n");
1321     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1322     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1324     /*
1325     GSList const * selection_objs = desktop->selection->list();
1326     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1328     }
1329     */
1330     desktop->selection->clear();
1332     // printf("Desktop, starting reconstruction end\n");
1335 /// Called when document rebuild is finished.
1336 static void
1337 _reconstruction_finish (SPDesktop * desktop)
1339     // printf("Desktop, finishing reconstruction\n");
1340     if (desktop->_reconstruction_old_layer_id == NULL)
1341         return;
1343     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1344     if (newLayer != NULL)
1345         desktop->setCurrentLayer(newLayer);
1347     g_free(desktop->_reconstruction_old_layer_id);
1348     desktop->_reconstruction_old_layer_id = NULL;
1349     // printf("Desktop, finishing reconstruction end\n");
1350     return;
1353 /**
1354  * Namedview_modified callback.
1355  */
1356 static void
1357 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1359     SPNamedView *nv=SP_NAMEDVIEW(obj);
1361     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1363         /* Recalculate snap distances */
1364         /* FIXME: why is the desktop getting involved in setting up something
1365         ** that is entirely to do with the namedview?
1366         */
1367         _update_snap_distances (desktop);
1369         /* Show/hide page background */
1370         if (nv->pagecolor & 0xff) {
1371             sp_canvas_item_show (desktop->table);
1372             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1373             sp_canvas_item_move_to_z (desktop->table, 0);
1374         } else {
1375             sp_canvas_item_hide (desktop->table);
1376         }
1378         /* Show/hide page border */
1379         if (nv->showborder) {
1380             // show
1381             sp_canvas_item_show (desktop->page_border);
1382             // set color and shadow
1383             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1384             if (nv->pageshadow) {
1385                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1386             }
1387             // place in the z-order stack
1388             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1389                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1390             } else {
1391                 int order = sp_canvas_item_order (desktop->page_border);
1392                 int morder = sp_canvas_item_order (desktop->drawing);
1393                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1394                                     morder - order);
1395             }
1396         } else {
1397                 sp_canvas_item_hide (desktop->page_border);
1398                 if (nv->pageshadow) {
1399                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1400                 }
1401         }
1402         
1403         /* Show/hide page shadow */
1404         if (nv->showpageshadow && nv->pageshadow) {
1405             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1406         } else {
1407             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1408         }
1410         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1411             (SP_RGBA32_R_U(nv->pagecolor) +
1412              SP_RGBA32_G_U(nv->pagecolor) +
1413              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1414             // the background color is light or transparent, use black outline
1415             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1416         } else { // use white outline
1417             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1418         }
1419     }
1422 /**
1423  * Callback to reset snapper's distances.
1424  */
1425 static void
1426 _update_snap_distances (SPDesktop *desktop)
1428     SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1430     SPNamedView &nv = *desktop->namedview;
1432     
1433     // FIXME GRID: make one gridsnapper object that snaps to all enabled grids by calling their snappers.
1434     nv.snap_manager.grid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1435                                                                       *nv.gridtoleranceunit,
1436                                                                       px));
1437     //new grid snappers
1438     for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1439         Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1440         grid->snapper->setDistance(sp_convert_distance_full(nv.gridtolerance,
1441                                                                       *nv.gridtoleranceunit,
1442                                                                       px));
1443     }
1444     
1445     nv.snap_manager.guide.setDistance(sp_convert_distance_full(nv.guidetolerance,
1446                                                                        *nv.guidetoleranceunit,
1447                                                                        px));
1448     nv.snap_manager.object.setDistance(sp_convert_distance_full(nv.objecttolerance,
1449                                                                         *nv.objecttoleranceunit,
1450                                                                         px));
1454 NR::Matrix SPDesktop::w2d() const
1456     return _w2d;
1459 NR::Point SPDesktop::w2d(NR::Point const &p) const
1461     return p * _w2d;
1464 NR::Point SPDesktop::d2w(NR::Point const &p) const
1466     return p * _d2w;
1469 NR::Matrix SPDesktop::doc2dt() const
1471     return _doc2dt;
1474 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1476     return p * _doc2dt;
1479 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1481     return p / _doc2dt;
1485 /**
1486  * Pop event context from desktop's context stack. Never used.
1487  */
1488 // void
1489 // SPDesktop::pop_event_context (unsigned int key)
1490 // {
1491 //    SPEventContext *ec = NULL;
1492 //
1493 //    if (event_context && event_context->key == key) {
1494 //        g_return_if_fail (event_context);
1495 //        g_return_if_fail (event_context->next);
1496 //        ec = event_context;
1497 //        sp_event_context_deactivate (ec);
1498 //        event_context = ec->next;
1499 //        sp_event_context_activate (event_context);
1500 //        _event_context_changed_signal.emit (this, ec);
1501 //    }
1502 //
1503 //    SPEventContext *ref = event_context;
1504 //    while (ref && ref->next && ref->next->key != key)
1505 //        ref = ref->next;
1506 //
1507 //    if (ref && ref->next) {
1508 //        ec = ref->next;
1509 //        ref->next = ec->next;
1510 //    }
1511 //
1512 //    if (ec) {
1513 //        sp_event_context_finish (ec);
1514 //        g_object_unref (G_OBJECT (ec));
1515 //    }
1516 // }
1518 /*
1519   Local Variables:
1520   mode:c++
1521   c-file-style:"stroustrup"
1522   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1523   indent-tabs-mode:nil
1524   fill-column:99
1525   End:
1526 */
1527 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :