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"
87 namespace Inkscape { namespace XML { class Node; }}
89 // Callback declarations
90 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
91 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
92 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
93 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
94 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
95 static void _reconstruction_start(SPDesktop * desktop);
96 static void _reconstruction_finish(SPDesktop * desktop);
97 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
98 static void _update_snap_distances (SPDesktop *desktop);
100 /**
101 * Return new desktop object.
102 * \pre namedview != NULL.
103 * \pre canvas != NULL.
104 */
105 SPDesktop::SPDesktop()
106 {
107 _dlg_mgr = NULL;
108 _widget = 0;
109 namedview = NULL;
110 selection = NULL;
111 acetate = NULL;
112 main = NULL;
113 grid = NULL;
114 guides = NULL;
115 drawing = NULL;
116 sketch = NULL;
117 controls = NULL;
118 event_context = 0;
119 layer_manager = 0;
121 _d2w.set_identity();
122 _w2d.set_identity();
123 _doc2dt = NR::Matrix(NR::scale(1, -1));
125 guides_active = false;
127 zooms_past = NULL;
128 zooms_future = NULL;
130 is_fullscreen = false;
131 waiting_cursor = false;
133 gr_item = NULL;
134 gr_point_type = 0;
135 gr_point_i = 0;
136 gr_fill_or_stroke = true;
138 _layer_hierarchy = NULL;
139 _active = false;
141 selection = Inkscape::GC::release (new Inkscape::Selection (this));
142 }
144 void
145 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
147 {
148 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
150 current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
152 namedview = nv;
153 canvas = aCanvas;
155 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
156 /* Kill flicker */
157 sp_document_ensure_up_to_date (document);
159 /* Setup Dialog Manager */
160 _dlg_mgr = new Inkscape::UI::Dialog::DialogManager();
162 dkey = sp_item_display_key_new (1);
164 /* Connect document */
165 setDocument (document);
167 number = namedview->getViewCount();
170 /* Setup Canvas */
171 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
173 SPCanvasGroup *root = sp_canvas_root (canvas);
175 /* Setup adminstrative layers */
176 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
177 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
178 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
179 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
181 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
182 SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
183 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
184 sp_canvas_item_move_to_z (table, 0);
186 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
187 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
188 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
190 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
191 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
193 SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
195 if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
196 // Start in outline mode
197 setDisplayModeOutline();
198 } else {
199 // Start in normal mode, default
200 setDisplayModeNormal();
201 }
203 grid = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
204 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
205 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
206 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
208 /* Push select tool to the bottom of stack */
209 /** \todo
210 * FIXME: this is the only call to this. Everything else seems to just
211 * call "set" instead of "push". Can we assume that there is only one
212 * context ever?
213 */
214 push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
216 // display rect and zoom are now handled in sp_desktop_widget_realize()
218 NR::Rect const d(NR::Point(0.0, 0.0),
219 NR::Point(sp_document_width(document), sp_document_height(document)));
221 SP_CTRLRECT(page)->setRectangle(d);
222 SP_CTRLRECT(page_border)->setRectangle(d);
224 /* the following sets the page shadow on the canvas
225 It was originally set to 5, which is really cheesy!
226 It now is an attribute in the document's namedview. If a value of
227 0 is used, then the constructor for a shadow is not initialized.
228 */
230 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
231 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
232 }
235 /* Connect event for page resize */
236 _doc2dt[5] = sp_document_height (document);
237 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
239 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
241 NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
242 SP_CANVAS_ARENA (drawing)->arena,
243 dkey,
244 SP_ITEM_SHOW_DISPLAY);
245 if (ai) {
246 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
247 nr_arena_item_unref (ai);
248 }
250 namedview->show(this);
251 /* Ugly hack */
252 activate_guides (true);
253 /* Ugly hack */
254 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
256 /* Set up notification of rebuilding the document, this allows
257 for saving object related settings in the document. */
258 _reconstruction_start_connection =
259 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
260 _reconstruction_finish_connection =
261 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
262 _reconstruction_old_layer_id = NULL;
264 _commit_connection = document->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
266 // ?
267 // sp_active_desktop_set (desktop);
268 _inkscape = INKSCAPE;
270 _activate_connection = _activate_signal.connect(
271 sigc::bind(
272 sigc::ptr_fun(_onActivate),
273 this
274 )
275 );
276 _deactivate_connection = _deactivate_signal.connect(
277 sigc::bind(
278 sigc::ptr_fun(_onDeactivate),
279 this
280 )
281 );
283 _sel_modified_connection = selection->connectModified(
284 sigc::bind(
285 sigc::ptr_fun(&_onSelectionModified),
286 this
287 )
288 );
289 _sel_changed_connection = selection->connectChanged(
290 sigc::bind(
291 sigc::ptr_fun(&_onSelectionChanged),
292 this
293 )
294 );
297 /* setup LayerManager */
298 // (Setting up after the connections are all in place, as it may use some of them)
299 layer_manager = new Inkscape::LayerManager( this );
300 }
303 void SPDesktop::destroy()
304 {
305 _activate_connection.disconnect();
306 _deactivate_connection.disconnect();
307 _sel_modified_connection.disconnect();
308 _sel_changed_connection.disconnect();
309 _modified_connection.disconnect();
310 _commit_connection.disconnect();
311 _reconstruction_start_connection.disconnect();
312 _reconstruction_finish_connection.disconnect();
314 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
315 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
316 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
318 while (event_context) {
319 SPEventContext *ec = event_context;
320 event_context = ec->next;
321 sp_event_context_finish (ec);
322 g_object_unref (G_OBJECT (ec));
323 }
325 if (_layer_hierarchy) {
326 delete _layer_hierarchy;
327 }
329 if (_inkscape) {
330 _inkscape = NULL;
331 }
333 if (drawing) {
334 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
335 drawing = NULL;
336 }
338 delete _guides_message_context;
339 _guides_message_context = NULL;
341 g_list_free (zooms_past);
342 g_list_free (zooms_future);
343 }
345 SPDesktop::~SPDesktop() {}
347 //--------------------------------------------------------------------
348 /* Public methods */
350 void SPDesktop::setDisplayModeNormal()
351 {
352 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
353 canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
354 displayMode = RENDERMODE_NORMAL;
355 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
356 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
357 }
359 void SPDesktop::setDisplayModeOutline()
360 {
361 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
362 canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
363 displayMode = RENDERMODE_OUTLINE;
364 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
365 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
366 }
368 void SPDesktop::displayModeToggle()
369 {
370 if (displayMode == RENDERMODE_OUTLINE)
371 setDisplayModeNormal();
372 else
373 setDisplayModeOutline();
374 }
376 /**
377 * Returns current root (=bottom) layer.
378 */
379 SPObject *SPDesktop::currentRoot() const
380 {
381 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
382 }
384 /**
385 * Returns current top layer.
386 */
387 SPObject *SPDesktop::currentLayer() const
388 {
389 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
390 }
392 /**
393 * Sets the current layer of the desktop.
394 *
395 * Make \a object the top layer.
396 */
397 void SPDesktop::setCurrentLayer(SPObject *object) {
398 g_return_if_fail(SP_IS_GROUP(object));
399 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
400 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
401 _layer_hierarchy->setBottom(object);
402 }
404 /**
405 * Return layer that contains \a object.
406 */
407 SPObject *SPDesktop::layerForObject(SPObject *object) {
408 g_return_val_if_fail(object != NULL, NULL);
410 SPObject *root=currentRoot();
411 object = SP_OBJECT_PARENT(object);
412 while ( object && object != root && !isLayer(object) ) {
413 object = SP_OBJECT_PARENT(object);
414 }
415 return object;
416 }
418 /**
419 * True if object is a layer.
420 */
421 bool SPDesktop::isLayer(SPObject *object) const {
422 return ( SP_IS_GROUP(object)
423 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
424 == SPGroup::LAYER ) );
425 }
427 /**
428 * True if desktop viewport fully contains \a item's bbox.
429 */
430 bool SPDesktop::isWithinViewport (SPItem *item) const
431 {
432 NR::Rect const viewport = get_display_area();
433 NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
434 if (bbox) {
435 return viewport.contains(*bbox);
436 } else {
437 return true;
438 }
439 }
441 ///
442 bool SPDesktop::itemIsHidden(SPItem const *item) const {
443 return item->isHidden(this->dkey);
444 }
446 /**
447 * Set activate property of desktop; emit signal if changed.
448 */
449 void
450 SPDesktop::set_active (bool new_active)
451 {
452 if (new_active != _active) {
453 _active = new_active;
454 if (new_active) {
455 _activate_signal.emit();
456 } else {
457 _deactivate_signal.emit();
458 }
459 }
460 }
462 /**
463 * Set activate status of current desktop's named view.
464 */
465 void
466 SPDesktop::activate_guides(bool activate)
467 {
468 guides_active = activate;
469 namedview->activateGuides(this, activate);
470 }
472 /**
473 * Make desktop switch documents.
474 */
475 void
476 SPDesktop::change_document (SPDocument *theDocument)
477 {
478 g_return_if_fail (theDocument != NULL);
480 /* unselect everything before switching documents */
481 selection->clear();
483 setDocument (theDocument);
484 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
485 _document_replaced_signal.emit (this, theDocument);
486 }
488 /**
489 * Make desktop switch event contexts.
490 */
491 void
492 SPDesktop::set_event_context (GtkType type, const gchar *config)
493 {
494 SPEventContext *ec;
495 while (event_context) {
496 ec = event_context;
497 sp_event_context_deactivate (ec);
498 event_context = ec->next;
499 sp_event_context_finish (ec);
500 g_object_unref (G_OBJECT (ec));
501 }
503 Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
504 ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
505 ec->next = event_context;
506 event_context = ec;
507 sp_event_context_activate (ec);
508 _event_context_changed_signal.emit (this, ec);
509 }
511 /**
512 * Push event context onto desktop's context stack.
513 */
514 void
515 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
516 {
517 SPEventContext *ref, *ec;
518 Inkscape::XML::Node *repr;
520 if (event_context && event_context->key == key) return;
521 ref = event_context;
522 while (ref && ref->next && ref->next->key != key) ref = ref->next;
523 if (ref && ref->next) {
524 ec = ref->next;
525 ref->next = ec->next;
526 sp_event_context_finish (ec);
527 g_object_unref (G_OBJECT (ec));
528 }
530 if (event_context) sp_event_context_deactivate (event_context);
531 repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
532 ec = sp_event_context_new (type, this, repr, key);
533 ec->next = event_context;
534 event_context = ec;
535 sp_event_context_activate (ec);
536 _event_context_changed_signal.emit (this, ec);
537 }
539 /**
540 * Sets the coordinate status to a given point
541 */
542 void
543 SPDesktop::set_coordinate_status (NR::Point p) {
544 _widget->setCoordinateStatus(p);
545 }
547 /**
548 * \see sp_document_item_from_list_at_point_bottom()
549 */
550 SPItem *
551 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
552 {
553 g_return_val_if_fail (doc() != NULL, NULL);
554 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
555 }
557 /**
558 * \see sp_document_item_at_point()
559 */
560 SPItem *
561 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
562 {
563 g_return_val_if_fail (doc() != NULL, NULL);
564 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
565 }
567 /**
568 * \see sp_document_group_at_point()
569 */
570 SPItem *
571 SPDesktop::group_at_point (NR::Point const p) const
572 {
573 g_return_val_if_fail (doc() != NULL, NULL);
574 return sp_document_group_at_point (doc(), dkey, p);
575 }
577 /**
578 * \brief Returns the mouse point in document coordinates; if mouse is
579 * outside the canvas, returns the center of canvas viewpoint
580 */
581 NR::Point
582 SPDesktop::point() const
583 {
584 NR::Point p = _widget->getPointer();
585 NR::Point pw = sp_canvas_window_to_world (canvas, p);
586 p = w2d(pw);
588 NR::Rect const r = canvas->getViewbox();
590 NR::Point r0 = w2d(r.min());
591 NR::Point r1 = w2d(r.max());
593 if (p[NR::X] >= r0[NR::X] &&
594 p[NR::X] <= r1[NR::X] &&
595 p[NR::Y] >= r1[NR::Y] &&
596 p[NR::Y] <= r0[NR::Y])
597 {
598 return p;
599 } else {
600 return (r0 + r1) / 2;
601 }
602 }
604 /**
605 * Put current zoom data in history list.
606 */
607 void
608 SPDesktop::push_current_zoom (GList **history)
609 {
610 NR::Rect const area = get_display_area();
612 NRRect *old_zoom = g_new(NRRect, 1);
613 old_zoom->x0 = area.min()[NR::X];
614 old_zoom->x1 = area.max()[NR::X];
615 old_zoom->y0 = area.min()[NR::Y];
616 old_zoom->y1 = area.max()[NR::Y];
617 if ( *history == NULL
618 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
619 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
620 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
621 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
622 {
623 *history = g_list_prepend (*history, old_zoom);
624 }
625 }
627 /**
628 * Set viewbox.
629 */
630 void
631 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
632 {
633 g_assert(_widget);
635 // save the zoom
636 if (log) {
637 push_current_zoom(&zooms_past);
638 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
639 g_list_free (zooms_future);
640 zooms_future = NULL;
641 }
643 double const cx = 0.5 * (x0 + x1);
644 double const cy = 0.5 * (y0 + y1);
646 NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
648 double scale = expansion(_d2w);
649 double newscale;
650 if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
651 newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
652 } else {
653 newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
654 }
656 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
658 int clear = FALSE;
659 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
660 /* Set zoom factors */
661 _d2w = NR::Matrix(NR::scale(newscale, -newscale));
662 _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
663 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
664 clear = TRUE;
665 }
667 /* Calculate top left corner */
668 x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
669 y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
671 /* Scroll */
672 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
674 _widget->updateRulers();
675 _widget->updateScrollbars(expansion(_d2w));
676 _widget->updateZoom();
677 }
679 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
680 {
681 set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
682 }
684 /**
685 * Return viewbox dimensions.
686 */
687 NR::Rect SPDesktop::get_display_area() const
688 {
689 NR::Rect const viewbox = canvas->getViewbox();
691 double const scale = _d2w[0];
693 return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
694 NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
695 }
697 /**
698 * Revert back to previous zoom if possible.
699 */
700 void
701 SPDesktop::prev_zoom()
702 {
703 if (zooms_past == NULL) {
704 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
705 return;
706 }
708 // push current zoom into forward zooms list
709 push_current_zoom (&zooms_future);
711 // restore previous zoom
712 set_display_area (((NRRect *) zooms_past->data)->x0,
713 ((NRRect *) zooms_past->data)->y0,
714 ((NRRect *) zooms_past->data)->x1,
715 ((NRRect *) zooms_past->data)->y1,
716 0, false);
718 // remove the just-added zoom from the past zooms list
719 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
720 }
722 /**
723 * Set zoom to next in list.
724 */
725 void
726 SPDesktop::next_zoom()
727 {
728 if (zooms_future == NULL) {
729 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
730 return;
731 }
733 // push current zoom into past zooms list
734 push_current_zoom (&zooms_past);
736 // restore next zoom
737 set_display_area (((NRRect *) zooms_future->data)->x0,
738 ((NRRect *) zooms_future->data)->y0,
739 ((NRRect *) zooms_future->data)->x1,
740 ((NRRect *) zooms_future->data)->y1,
741 0, false);
743 // remove the just-used zoom from the zooms_future list
744 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
745 }
747 /**
748 * Zoom to point with absolute zoom factor.
749 */
750 void
751 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
752 {
753 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
755 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
756 // this check prevents "sliding" when trying to zoom in at maximum zoom;
757 /// \todo someone please fix calculations properly and remove this hack
758 if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
759 return;
761 NR::Rect const viewbox = canvas->getViewbox();
763 double const width2 = viewbox.dimensions()[NR::X] / zoom;
764 double const height2 = viewbox.dimensions()[NR::Y] / zoom;
766 set_display_area(cx - px * width2,
767 cy - py * height2,
768 cx + (1 - px) * width2,
769 cy + (1 - py) * height2,
770 0.0);
771 }
773 /**
774 * Zoom to center with absolute zoom factor.
775 */
776 void
777 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
778 {
779 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
780 }
782 /**
783 * Zoom to point with relative zoom factor.
784 */
785 void
786 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
787 {
788 NR::Rect const area = get_display_area();
790 if (cx < area.min()[NR::X]) {
791 cx = area.min()[NR::X];
792 }
793 if (cx > area.max()[NR::X]) {
794 cx = area.max()[NR::X];
795 }
796 if (cy < area.min()[NR::Y]) {
797 cy = area.min()[NR::Y];
798 }
799 if (cy > area.max()[NR::Y]) {
800 cy = area.max()[NR::Y];
801 }
803 gdouble const scale = expansion(_d2w) * zoom;
804 double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
805 double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
807 zoom_absolute_keep_point(cx, cy, px, py, scale);
808 }
810 /**
811 * Zoom to center with relative zoom factor.
812 */
813 void
814 SPDesktop::zoom_relative (double cx, double cy, double zoom)
815 {
816 gdouble scale = expansion(_d2w) * zoom;
817 zoom_absolute (cx, cy, scale);
818 }
820 /**
821 * Set display area to origin and current document dimensions.
822 */
823 void
824 SPDesktop::zoom_page()
825 {
826 NR::Rect d(NR::Point(0, 0),
827 NR::Point(sp_document_width(doc()), sp_document_height(doc())));
829 if (d.isEmpty(1.0)) {
830 return;
831 }
833 set_display_area(d, 10);
834 }
836 /**
837 * Set display area to current document width.
838 */
839 void
840 SPDesktop::zoom_page_width()
841 {
842 NR::Rect const a = get_display_area();
844 if (sp_document_width(doc()) < 1.0) {
845 return;
846 }
848 NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
849 NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
851 set_display_area(d, 10);
852 }
854 /**
855 * Zoom to selection.
856 */
857 void
858 SPDesktop::zoom_selection()
859 {
860 NR::Maybe<NR::Rect> const d = selection->bounds();
862 if ( !d || d->isEmpty(0.1) ) {
863 return;
864 }
866 set_display_area(*d, 10);
867 }
869 /**
870 * Tell widget to let zoom widget grab keyboard focus.
871 */
872 void
873 SPDesktop::zoom_grab_focus()
874 {
875 _widget->letZoomGrabFocus();
876 }
878 /**
879 * Zoom to whole drawing.
880 */
881 void
882 SPDesktop::zoom_drawing()
883 {
884 g_return_if_fail (doc() != NULL);
885 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
886 g_return_if_fail (docitem != NULL);
888 NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
890 /* Note that the second condition here indicates that
891 ** there are no items in the drawing.
892 */
893 if ( !d || d->isEmpty(1.0) ) {
894 return;
895 }
897 set_display_area(*d, 10);
898 }
900 /**
901 * Scroll canvas by specific coordinate amount.
902 */
903 void
904 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
905 {
906 g_assert(_widget);
908 NR::Rect const viewbox = canvas->getViewbox();
910 sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
912 _widget->updateRulers();
913 _widget->updateScrollbars(expansion(_d2w));
914 }
916 bool
917 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
918 {
919 gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
921 // autoscrolldistance is in screen pixels, but the display area is in document units
922 autoscrolldistance /= expansion(_d2w);
923 NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
925 if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
926 !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y]) ) {
928 NR::Point const s_w( (*p) * _d2w );
930 gdouble x_to;
931 if ((*p)[NR::X] < dbox.min()[NR::X])
932 x_to = dbox.min()[NR::X];
933 else if ((*p)[NR::X] > dbox.max()[NR::X])
934 x_to = dbox.max()[NR::X];
935 else
936 x_to = (*p)[NR::X];
938 gdouble y_to;
939 if ((*p)[NR::Y] < dbox.min()[NR::Y])
940 y_to = dbox.min()[NR::Y];
941 else if ((*p)[NR::Y] > dbox.max()[NR::Y])
942 y_to = dbox.max()[NR::Y];
943 else
944 y_to = (*p)[NR::Y];
946 NR::Point const d_dt(x_to, y_to);
947 NR::Point const d_w( d_dt * _d2w );
948 NR::Point const moved_w( d_w - s_w );
950 if (autoscrollspeed == 0)
951 autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
953 if (autoscrollspeed != 0)
954 scroll_world (autoscrollspeed * moved_w);
956 return true;
957 }
958 return false;
959 }
961 void
962 SPDesktop::fullscreen()
963 {
964 _widget->setFullscreen();
965 }
967 void
968 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
969 {
970 _widget->getGeometry (x, y, w, h);
971 }
973 void
974 SPDesktop::setWindowPosition (NR::Point p)
975 {
976 _widget->setPosition (p);
977 }
979 void
980 SPDesktop::setWindowSize (gint w, gint h)
981 {
982 _widget->setSize (w, h);
983 }
985 void
986 SPDesktop::setWindowTransient (void *p, int transient_policy)
987 {
988 _widget->setTransient (p, transient_policy);
989 }
991 void
992 SPDesktop::presentWindow()
993 {
994 _widget->present();
995 }
997 bool
998 SPDesktop::warnDialog (gchar *text)
999 {
1000 return _widget->warnDialog (text);
1001 }
1003 void
1004 SPDesktop::toggleRulers()
1005 {
1006 _widget->toggleRulers();
1007 }
1009 void
1010 SPDesktop::toggleScrollbars()
1011 {
1012 _widget->toggleScrollbars();
1013 }
1015 void
1016 SPDesktop::layoutWidget()
1017 {
1018 _widget->layout();
1019 }
1021 void
1022 SPDesktop::destroyWidget()
1023 {
1024 _widget->destroy();
1025 }
1027 bool
1028 SPDesktop::shutdown()
1029 {
1030 return _widget->shutdown();
1031 }
1033 void
1034 SPDesktop::setToolboxFocusTo (gchar const *label)
1035 {
1036 _widget->setToolboxFocusTo (label);
1037 }
1039 void
1040 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1041 {
1042 _widget->setToolboxAdjustmentValue (id, val);
1043 }
1045 bool
1046 SPDesktop::isToolboxButtonActive (gchar const *id)
1047 {
1048 return _widget->isToolboxButtonActive (id);
1049 }
1051 void
1052 SPDesktop::emitToolSubselectionChanged(gpointer data)
1053 {
1054 _tool_subselection_changed.emit(data);
1055 inkscape_subselection_changed (this);
1056 }
1058 void
1059 SPDesktop::updateNow()
1060 {
1061 sp_canvas_update_now(canvas);
1062 }
1064 void
1065 SPDesktop::enableInteraction()
1066 {
1067 _widget->enableInteraction();
1068 }
1070 void SPDesktop::disableInteraction()
1071 {
1072 _widget->disableInteraction();
1073 }
1075 void SPDesktop::setWaitingCursor()
1076 {
1077 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1078 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1079 gdk_cursor_unref(waiting);
1080 waiting_cursor = true;
1082 // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1083 // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1084 // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1085 // after the call to setWaitingCursor as it was before
1086 while( Gtk::Main::events_pending() )
1087 Gtk::Main::iteration();
1088 }
1090 //----------------------------------------------------------------------
1091 // Callback implementations. The virtual ones are connected by the view.
1093 void
1094 SPDesktop::onPositionSet (double x, double y)
1095 {
1096 _widget->viewSetPosition (NR::Point(x,y));
1097 }
1099 void
1100 SPDesktop::onResized (double x, double y)
1101 {
1102 // Nothing called here
1103 }
1105 /**
1106 * Redraw callback; queues Gtk redraw; connected by View.
1107 */
1108 void
1109 SPDesktop::onRedrawRequested ()
1110 {
1111 if (main) {
1112 _widget->requestCanvasUpdate();
1113 }
1114 }
1116 void
1117 SPDesktop::updateCanvasNow()
1118 {
1119 _widget->requestCanvasUpdateAndWait();
1120 }
1122 /**
1123 * Associate document with desktop.
1124 */
1125 /// \todo fixme: refactor SPDesktop::init to use setDocument
1126 void
1127 SPDesktop::setDocument (SPDocument *doc)
1128 {
1129 if (this->doc() && doc) {
1130 namedview->hide(this);
1131 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1132 }
1134 if (_layer_hierarchy) {
1135 _layer_hierarchy->clear();
1136 delete _layer_hierarchy;
1137 }
1138 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1139 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1140 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1141 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1142 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1144 /* setup EventLog */
1145 event_log = new Inkscape::EventLog(doc);
1146 doc->addUndoObserver(*event_log);
1148 _commit_connection.disconnect();
1149 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1151 /// \todo fixme: This condition exists to make sure the code
1152 /// inside is called only once on initialization. But there
1153 /// are surely more safe methods to accomplish this.
1154 if (drawing) {
1155 NRArenaItem *ai;
1157 namedview = sp_document_namedview (doc, NULL);
1158 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1159 number = namedview->getViewCount();
1161 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1162 SP_CANVAS_ARENA (drawing)->arena,
1163 dkey,
1164 SP_ITEM_SHOW_DISPLAY);
1165 if (ai) {
1166 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1167 nr_arena_item_unref (ai);
1168 }
1169 namedview->show(this);
1170 /* Ugly hack */
1171 activate_guides (true);
1172 /* Ugly hack */
1173 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1174 }
1176 _document_replaced_signal.emit (this, doc);
1178 View::setDocument (doc);
1179 }
1181 void
1182 SPDesktop::onStatusMessage
1183 (Inkscape::MessageType type, gchar const *message)
1184 {
1185 if (_widget) {
1186 _widget->setMessage(type, message);
1187 }
1188 }
1190 void
1191 SPDesktop::onDocumentURISet (gchar const* uri)
1192 {
1193 _widget->setTitle(uri);
1194 }
1196 /**
1197 * Resized callback.
1198 */
1199 void
1200 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1201 {
1202 _doc2dt[5] = height;
1203 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1204 NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1205 SP_CTRLRECT(page)->setRectangle(a);
1206 SP_CTRLRECT(page_border)->setRectangle(a);
1207 }
1210 void
1211 SPDesktop::_onActivate (SPDesktop* dt)
1212 {
1213 if (!dt->_widget) return;
1214 dt->_widget->activateDesktop();
1215 }
1217 void
1218 SPDesktop::_onDeactivate (SPDesktop* dt)
1219 {
1220 if (!dt->_widget) return;
1221 dt->_widget->deactivateDesktop();
1222 }
1224 void
1225 SPDesktop::_onSelectionModified
1226 (Inkscape::Selection *selection, guint flags, SPDesktop *dt)
1227 {
1228 if (!dt->_widget) return;
1229 dt->_widget->updateScrollbars (expansion(dt->_d2w));
1230 }
1232 static void
1233 _onSelectionChanged
1234 (Inkscape::Selection *selection, SPDesktop *desktop)
1235 {
1236 /** \todo
1237 * only change the layer for single selections, or what?
1238 * This seems reasonable -- for multiple selections there can be many
1239 * different layers involved.
1240 */
1241 SPItem *item=selection->singleItem();
1242 if (item) {
1243 SPObject *layer=desktop->layerForObject(item);
1244 if ( layer && layer != desktop->currentLayer() ) {
1245 desktop->setCurrentLayer(layer);
1246 }
1247 }
1248 }
1250 /**
1251 * Calls event handler of current event context.
1252 * \param arena Unused
1253 * \todo fixme
1254 */
1255 static gint
1256 _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1257 {
1258 if (ai) {
1259 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1260 return sp_event_context_item_handler (desktop->event_context, spi, event);
1261 } else {
1262 return sp_event_context_root_handler (desktop->event_context, event);
1263 }
1264 }
1266 static void
1267 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1268 g_return_if_fail(SP_IS_GROUP(layer));
1269 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1270 }
1272 /// Callback
1273 static void
1274 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1275 g_return_if_fail(SP_IS_GROUP(layer));
1276 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1277 }
1279 /// Callback
1280 static void
1281 _layer_hierarchy_changed(SPObject *top, SPObject *bottom,
1282 SPDesktop *desktop)
1283 {
1284 desktop->_layer_changed_signal.emit (bottom);
1285 }
1287 /// Called when document is starting to be rebuilt.
1288 static void
1289 _reconstruction_start (SPDesktop * desktop)
1290 {
1291 // printf("Desktop, starting reconstruction\n");
1292 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1293 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1295 /*
1296 GSList const * selection_objs = desktop->selection->list();
1297 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1299 }
1300 */
1301 desktop->selection->clear();
1303 // printf("Desktop, starting reconstruction end\n");
1304 }
1306 /// Called when document rebuild is finished.
1307 static void
1308 _reconstruction_finish (SPDesktop * desktop)
1309 {
1310 // printf("Desktop, finishing reconstruction\n");
1311 if (desktop->_reconstruction_old_layer_id == NULL)
1312 return;
1314 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1315 if (newLayer != NULL)
1316 desktop->setCurrentLayer(newLayer);
1318 g_free(desktop->_reconstruction_old_layer_id);
1319 desktop->_reconstruction_old_layer_id = NULL;
1320 // printf("Desktop, finishing reconstruction end\n");
1321 return;
1322 }
1324 /**
1325 * Namedview_modified callback.
1326 */
1327 static void
1328 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1329 {
1330 SPNamedView *nv=SP_NAMEDVIEW(obj);
1332 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1334 /* Recalculate snap distances */
1335 /* FIXME: why is the desktop getting involved in setting up something
1336 ** that is entirely to do with the namedview?
1337 */
1338 _update_snap_distances (desktop);
1340 /* Show/hide page background */
1341 if (nv->pagecolor & 0xff) {
1342 sp_canvas_item_show (desktop->table);
1343 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1344 sp_canvas_item_move_to_z (desktop->table, 0);
1345 } else {
1346 sp_canvas_item_hide (desktop->table);
1347 }
1349 /* Show/hide page border */
1350 if (nv->showborder) {
1351 // show
1352 sp_canvas_item_show (desktop->page_border);
1353 // set color and shadow
1354 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1355 if (nv->pageshadow) {
1356 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1357 }
1358 // place in the z-order stack
1359 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1360 sp_canvas_item_move_to_z (desktop->page_border, 2);
1361 } else {
1362 int order = sp_canvas_item_order (desktop->page_border);
1363 int morder = sp_canvas_item_order (desktop->drawing);
1364 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1365 morder - order);
1366 }
1367 } else {
1368 sp_canvas_item_hide (desktop->page_border);
1369 if (nv->pageshadow) {
1370 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1371 }
1372 }
1374 /* Show/hide page shadow */
1375 if (nv->showpageshadow && nv->pageshadow) {
1376 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1377 } else {
1378 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1379 }
1381 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1382 (SP_RGBA32_R_U(nv->pagecolor) +
1383 SP_RGBA32_G_U(nv->pagecolor) +
1384 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1385 // the background color is light or transparent, use black outline
1386 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1387 } else { // use white outline
1388 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1389 }
1390 }
1391 }
1393 /**
1394 * Callback to reset snapper's distances.
1395 */
1396 static void
1397 _update_snap_distances (SPDesktop *desktop)
1398 {
1399 SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1401 SPNamedView &nv = *desktop->namedview;
1403 nv.snap_manager.grid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1404 *nv.gridtoleranceunit,
1405 px));
1406 nv.snap_manager.axonomgrid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1407 *nv.gridtoleranceunit,
1408 px));
1409 nv.snap_manager.guide.setDistance(sp_convert_distance_full(nv.guidetolerance,
1410 *nv.guidetoleranceunit,
1411 px));
1412 nv.snap_manager.object.setDistance(sp_convert_distance_full(nv.objecttolerance,
1413 *nv.objecttoleranceunit,
1414 px));
1415 }
1418 NR::Matrix SPDesktop::w2d() const
1419 {
1420 return _w2d;
1421 }
1423 NR::Point SPDesktop::w2d(NR::Point const &p) const
1424 {
1425 return p * _w2d;
1426 }
1428 NR::Point SPDesktop::d2w(NR::Point const &p) const
1429 {
1430 return p * _d2w;
1431 }
1433 NR::Matrix SPDesktop::doc2dt() const
1434 {
1435 return _doc2dt;
1436 }
1438 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1439 {
1440 return p * _doc2dt;
1441 }
1443 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1444 {
1445 return p / _doc2dt;
1446 }
1449 /**
1450 * Pop event context from desktop's context stack. Never used.
1451 */
1452 // void
1453 // SPDesktop::pop_event_context (unsigned int key)
1454 // {
1455 // SPEventContext *ec = NULL;
1456 //
1457 // if (event_context && event_context->key == key) {
1458 // g_return_if_fail (event_context);
1459 // g_return_if_fail (event_context->next);
1460 // ec = event_context;
1461 // sp_event_context_deactivate (ec);
1462 // event_context = ec->next;
1463 // sp_event_context_activate (event_context);
1464 // _event_context_changed_signal.emit (this, ec);
1465 // }
1466 //
1467 // SPEventContext *ref = event_context;
1468 // while (ref && ref->next && ref->next->key != key)
1469 // ref = ref->next;
1470 //
1471 // if (ref && ref->next) {
1472 // ec = ref->next;
1473 // ref->next = ec->next;
1474 // }
1475 //
1476 // if (ec) {
1477 // sp_event_context_finish (ec);
1478 // g_object_unref (G_OBJECT (ec));
1479 // }
1480 // }
1482 /*
1483 Local Variables:
1484 mode:c++
1485 c-file-style:"stroustrup"
1486 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1487 indent-tabs-mode:nil
1488 fill-column:99
1489 End:
1490 */
1491 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :