Code

When no grid is defined but "toggle grid" is pressed => create a default rectangular...
[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 (namedview->grids) {
1109         if(gridgroup) {
1110             grids_visible = !grids_visible;
1111             if (grids_visible) {
1112                 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1113             } else {
1114                 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1115             }
1116         }
1117     } else {
1118         //there is no grid present at the moment. add a rectangular grid and make it visible
1119         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1120         Inkscape::CanvasGrid::writeNewGridToRepr(repr, "xygrid");
1121         grids_visible = true;
1122         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1123     }
1127 //----------------------------------------------------------------------
1128 // Callback implementations. The virtual ones are connected by the view.
1130 void
1131 SPDesktop::onPositionSet (double x, double y)
1133     _widget->viewSetPosition (NR::Point(x,y));
1136 void
1137 SPDesktop::onResized (double x, double y)
1139    // Nothing called here
1142 /**
1143  * Redraw callback; queues Gtk redraw; connected by View.
1144  */
1145 void
1146 SPDesktop::onRedrawRequested ()
1148     if (main) {
1149         _widget->requestCanvasUpdate();
1150     }
1153 void
1154 SPDesktop::updateCanvasNow()
1156   _widget->requestCanvasUpdateAndWait();
1159 /**
1160  * Associate document with desktop.
1161  */
1162 /// \todo fixme: refactor SPDesktop::init to use setDocument
1163 void
1164 SPDesktop::setDocument (SPDocument *doc)
1166     if (this->doc() && doc) {
1167         namedview->hide(this);
1168         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1169     }
1171     if (_layer_hierarchy) {
1172         _layer_hierarchy->clear();
1173         delete _layer_hierarchy;
1174     }
1175     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1176     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1177     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1178     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1179     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1181     /* setup EventLog */
1182     event_log = new Inkscape::EventLog(doc);
1183     doc->addUndoObserver(*event_log);
1185     _commit_connection.disconnect();
1186     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1188     /// \todo fixme: This condition exists to make sure the code
1189     /// inside is called only once on initialization. But there
1190     /// are surely more safe methods to accomplish this.
1191     if (drawing) {
1192         NRArenaItem *ai;
1194         namedview = sp_document_namedview (doc, NULL);
1195         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1196         number = namedview->getViewCount();
1198         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1199                 SP_CANVAS_ARENA (drawing)->arena,
1200                 dkey,
1201                 SP_ITEM_SHOW_DISPLAY);
1202         if (ai) {
1203             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1204             nr_arena_item_unref (ai);
1205         }
1206         namedview->show(this);
1207         /* Ugly hack */
1208         activate_guides (true);
1209         /* Ugly hack */
1210         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1211     }
1213     _document_replaced_signal.emit (this, doc);
1215     View::setDocument (doc);
1218 void
1219 SPDesktop::onStatusMessage
1220 (Inkscape::MessageType type, gchar const *message)
1222     if (_widget) {
1223         _widget->setMessage(type, message);
1224     }
1227 void
1228 SPDesktop::onDocumentURISet (gchar const* uri)
1230     _widget->setTitle(uri);
1233 /**
1234  * Resized callback.
1235  */
1236 void
1237 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1239     _doc2dt[5] = height;
1240     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1241     NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1242     SP_CTRLRECT(page)->setRectangle(a);
1243     SP_CTRLRECT(page_border)->setRectangle(a);
1247 void
1248 SPDesktop::_onActivate (SPDesktop* dt)
1250     if (!dt->_widget) return;
1251     dt->_widget->activateDesktop();
1254 void
1255 SPDesktop::_onDeactivate (SPDesktop* dt)
1257     if (!dt->_widget) return;
1258     dt->_widget->deactivateDesktop();
1261 void
1262 SPDesktop::_onSelectionModified
1263 (Inkscape::Selection *selection, guint flags, SPDesktop *dt)
1265     if (!dt->_widget) return;
1266     dt->_widget->updateScrollbars (expansion(dt->_d2w));
1269 static void
1270 _onSelectionChanged
1271 (Inkscape::Selection *selection, SPDesktop *desktop)
1273     /** \todo
1274      * only change the layer for single selections, or what?
1275      * This seems reasonable -- for multiple selections there can be many
1276      * different layers involved.
1277      */
1278     SPItem *item=selection->singleItem();
1279     if (item) {
1280         SPObject *layer=desktop->layerForObject(item);
1281         if ( layer && layer != desktop->currentLayer() ) {
1282             desktop->setCurrentLayer(layer);
1283         }
1284     }
1287 /**
1288  * Calls event handler of current event context.
1289  * \param arena Unused
1290  * \todo fixme
1291  */
1292 static gint
1293 _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1295     if (ai) {
1296         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1297         return sp_event_context_item_handler (desktop->event_context, spi, event);
1298     } else {
1299         return sp_event_context_root_handler (desktop->event_context, event);
1300     }
1303 static void
1304 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1305     g_return_if_fail(SP_IS_GROUP(layer));
1306     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1309 /// Callback
1310 static void
1311 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1312     g_return_if_fail(SP_IS_GROUP(layer));
1313     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1316 /// Callback
1317 static void
1318 _layer_hierarchy_changed(SPObject *top, SPObject *bottom,
1319                                          SPDesktop *desktop)
1321     desktop->_layer_changed_signal.emit (bottom);
1324 /// Called when document is starting to be rebuilt.
1325 static void
1326 _reconstruction_start (SPDesktop * desktop)
1328     // printf("Desktop, starting reconstruction\n");
1329     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1330     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1332     /*
1333     GSList const * selection_objs = desktop->selection->list();
1334     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1336     }
1337     */
1338     desktop->selection->clear();
1340     // printf("Desktop, starting reconstruction end\n");
1343 /// Called when document rebuild is finished.
1344 static void
1345 _reconstruction_finish (SPDesktop * desktop)
1347     // printf("Desktop, finishing reconstruction\n");
1348     if (desktop->_reconstruction_old_layer_id == NULL)
1349         return;
1351     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1352     if (newLayer != NULL)
1353         desktop->setCurrentLayer(newLayer);
1355     g_free(desktop->_reconstruction_old_layer_id);
1356     desktop->_reconstruction_old_layer_id = NULL;
1357     // printf("Desktop, finishing reconstruction end\n");
1358     return;
1361 /**
1362  * Namedview_modified callback.
1363  */
1364 static void
1365 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1367     SPNamedView *nv=SP_NAMEDVIEW(obj);
1369     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1371         /* Recalculate snap distances */
1372         /* FIXME: why is the desktop getting involved in setting up something
1373         ** that is entirely to do with the namedview?
1374         */
1375         _update_snap_distances (desktop);
1377         /* Show/hide page background */
1378         if (nv->pagecolor & 0xff) {
1379             sp_canvas_item_show (desktop->table);
1380             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1381             sp_canvas_item_move_to_z (desktop->table, 0);
1382         } else {
1383             sp_canvas_item_hide (desktop->table);
1384         }
1386         /* Show/hide page border */
1387         if (nv->showborder) {
1388             // show
1389             sp_canvas_item_show (desktop->page_border);
1390             // set color and shadow
1391             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1392             if (nv->pageshadow) {
1393                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1394             }
1395             // place in the z-order stack
1396             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1397                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1398             } else {
1399                 int order = sp_canvas_item_order (desktop->page_border);
1400                 int morder = sp_canvas_item_order (desktop->drawing);
1401                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1402                                     morder - order);
1403             }
1404         } else {
1405                 sp_canvas_item_hide (desktop->page_border);
1406                 if (nv->pageshadow) {
1407                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1408                 }
1409         }
1410         
1411         /* Show/hide page shadow */
1412         if (nv->showpageshadow && nv->pageshadow) {
1413             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1414         } else {
1415             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1416         }
1418         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1419             (SP_RGBA32_R_U(nv->pagecolor) +
1420              SP_RGBA32_G_U(nv->pagecolor) +
1421              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1422             // the background color is light or transparent, use black outline
1423             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1424         } else { // use white outline
1425             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1426         }
1427     }
1430 /**
1431  * Callback to reset snapper's distances.
1432  */
1433 static void
1434 _update_snap_distances (SPDesktop *desktop)
1436     SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1438     SPNamedView &nv = *desktop->namedview;
1440     //tell all grid snappers
1441     for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1442         Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1443         grid->snapper->setDistance(sp_convert_distance_full(nv.gridtolerance,
1444                                                                       *nv.gridtoleranceunit,
1445                                                                       px));
1446     }
1447     
1448     nv.snap_manager.guide.setDistance(sp_convert_distance_full(nv.guidetolerance,
1449                                                                        *nv.guidetoleranceunit,
1450                                                                        px));
1451     nv.snap_manager.object.setDistance(sp_convert_distance_full(nv.objecttolerance,
1452                                                                         *nv.objecttoleranceunit,
1453                                                                         px));
1457 NR::Matrix SPDesktop::w2d() const
1459     return _w2d;
1462 NR::Point SPDesktop::w2d(NR::Point const &p) const
1464     return p * _w2d;
1467 NR::Point SPDesktop::d2w(NR::Point const &p) const
1469     return p * _d2w;
1472 NR::Matrix SPDesktop::doc2dt() const
1474     return _doc2dt;
1477 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1479     return p * _doc2dt;
1482 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1484     return p / _doc2dt;
1488 /**
1489  * Pop event context from desktop's context stack. Never used.
1490  */
1491 // void
1492 // SPDesktop::pop_event_context (unsigned int key)
1493 // {
1494 //    SPEventContext *ec = NULL;
1495 //
1496 //    if (event_context && event_context->key == key) {
1497 //        g_return_if_fail (event_context);
1498 //        g_return_if_fail (event_context->next);
1499 //        ec = event_context;
1500 //        sp_event_context_deactivate (ec);
1501 //        event_context = ec->next;
1502 //        sp_event_context_activate (event_context);
1503 //        _event_context_changed_signal.emit (this, ec);
1504 //    }
1505 //
1506 //    SPEventContext *ref = event_context;
1507 //    while (ref && ref->next && ref->next->key != key)
1508 //        ref = ref->next;
1509 //
1510 //    if (ref && ref->next) {
1511 //        ec = ref->next;
1512 //        ref->next = ec->next;
1513 //    }
1514 //
1515 //    if (ec) {
1516 //        sp_event_context_finish (ec);
1517 //        g_object_unref (G_OBJECT (ec));
1518 //    }
1519 // }
1521 /*
1522   Local Variables:
1523   mode:c++
1524   c-file-style:"stroustrup"
1525   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1526   indent-tabs-mode:nil
1527   fill-column:99
1528   End:
1529 */
1530 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :