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>
58 #include "macros.h"
59 #include "inkscape-private.h"
60 #include "desktop.h"
61 #include "desktop-events.h"
62 #include "desktop-handles.h"
63 #include "document.h"
64 #include "message-stack.h"
65 #include "selection.h"
66 #include "select-context.h"
67 #include "sp-namedview.h"
68 #include "color.h"
69 #include "sp-item-group.h"
70 #include "prefs-utils.h"
71 #include "object-hierarchy.h"
72 #include "helper/units.h"
73 #include "display/canvas-arena.h"
74 #include "display/nr-arena.h"
75 #include "display/gnome-canvas-acetate.h"
76 #include "display/sodipodi-ctrlrect.h"
77 #include "display/sp-canvas-util.h"
78 #include "libnr/nr-matrix-div.h"
79 #include "libnr/nr-rect-ops.h"
80 #include "ui/dialog/dialog-manager.h"
81 #include "xml/repr.h"
82 #include "message-context.h"
83 #include "layer-manager.h"
84 #include "event-log.h"
86 namespace Inkscape { namespace XML { class Node; }}
88 // Callback declarations
89 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
90 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
91 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
92 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
93 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
94 static void _reconstruction_start(SPDesktop * desktop);
95 static void _reconstruction_finish(SPDesktop * desktop);
96 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
97 static void _update_snap_distances (SPDesktop *desktop);
99 /**
100 * Return new desktop object.
101 * \pre namedview != NULL.
102 * \pre canvas != NULL.
103 */
104 SPDesktop::SPDesktop()
105 {
106 _dlg_mgr = NULL;
107 _widget = 0;
108 namedview = NULL;
109 selection = NULL;
110 acetate = NULL;
111 main = NULL;
112 grid = NULL;
113 guides = NULL;
114 drawing = NULL;
115 sketch = NULL;
116 controls = NULL;
117 event_context = 0;
118 layer_manager = 0;
120 _d2w.set_identity();
121 _w2d.set_identity();
122 _doc2dt = NR::Matrix(NR::scale(1, -1));
124 guides_active = false;
126 zooms_past = NULL;
127 zooms_future = NULL;
129 is_fullscreen = false;
131 gr_item = NULL;
132 gr_point_type = 0;
133 gr_point_i = 0;
134 gr_fill_or_stroke = true;
136 _layer_hierarchy = NULL;
137 _active = false;
139 selection = Inkscape::GC::release (new Inkscape::Selection (this));
140 }
142 void
143 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
145 {
146 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
148 current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
150 namedview = nv;
151 canvas = aCanvas;
153 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
154 /* Kill flicker */
155 sp_document_ensure_up_to_date (document);
157 /* Setup Dialog Manager */
158 _dlg_mgr = new Inkscape::UI::Dialog::DialogManager();
160 dkey = sp_item_display_key_new (1);
162 /* Connect document */
163 setDocument (document);
165 number = namedview->getViewCount();
168 /* Setup Canvas */
169 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
171 SPCanvasGroup *root = sp_canvas_root (canvas);
173 /* Setup adminstrative layers */
174 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
175 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
176 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
177 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
179 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
180 SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
181 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
182 sp_canvas_item_move_to_z (table, 0);
184 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
185 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
186 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
188 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
189 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
191 SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
193 if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
194 // Start in outline mode
195 setDisplayModeOutline();
196 } else {
197 // Start in normal mode, default
198 setDisplayModeNormal();
199 }
201 grid = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
202 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
203 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
204 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
206 /* Push select tool to the bottom of stack */
207 /** \todo
208 * FIXME: this is the only call to this. Everything else seems to just
209 * call "set" instead of "push". Can we assume that there is only one
210 * context ever?
211 */
212 push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
214 // display rect and zoom are now handled in sp_desktop_widget_realize()
216 NR::Rect const d(NR::Point(0.0, 0.0),
217 NR::Point(sp_document_width(document), sp_document_height(document)));
219 SP_CTRLRECT(page)->setRectangle(d);
220 SP_CTRLRECT(page_border)->setRectangle(d);
222 /* the following sets the page shadow on the canvas
223 It was originally set to 5, which is really cheesy!
224 It now is an attribute in the document's namedview. If a value of
225 0 is used, then the constructor for a shadow is not initialized.
226 */
228 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
229 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
230 }
233 /* Connect event for page resize */
234 _doc2dt[5] = sp_document_height (document);
235 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
237 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
239 NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
240 SP_CANVAS_ARENA (drawing)->arena,
241 dkey,
242 SP_ITEM_SHOW_DISPLAY);
243 if (ai) {
244 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
245 nr_arena_item_unref (ai);
246 }
248 namedview->show(this);
249 /* Ugly hack */
250 activate_guides (true);
251 /* Ugly hack */
252 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
254 /* Set up notification of rebuilding the document, this allows
255 for saving object related settings in the document. */
256 _reconstruction_start_connection =
257 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
258 _reconstruction_finish_connection =
259 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
260 _reconstruction_old_layer_id = NULL;
262 _commit_connection = document->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
264 // ?
265 // sp_active_desktop_set (desktop);
266 _inkscape = INKSCAPE;
268 _activate_connection = _activate_signal.connect(
269 sigc::bind(
270 sigc::ptr_fun(_onActivate),
271 this
272 )
273 );
274 _deactivate_connection = _deactivate_signal.connect(
275 sigc::bind(
276 sigc::ptr_fun(_onDeactivate),
277 this
278 )
279 );
281 _sel_modified_connection = selection->connectModified(
282 sigc::bind(
283 sigc::ptr_fun(&_onSelectionModified),
284 this
285 )
286 );
287 _sel_changed_connection = selection->connectChanged(
288 sigc::bind(
289 sigc::ptr_fun(&_onSelectionChanged),
290 this
291 )
292 );
295 /* setup LayerManager */
296 // (Setting up after the connections are all in place, as it may use some of them)
297 layer_manager = new Inkscape::LayerManager( this );
298 }
301 void SPDesktop::destroy()
302 {
303 _activate_connection.disconnect();
304 _deactivate_connection.disconnect();
305 _sel_modified_connection.disconnect();
306 _sel_changed_connection.disconnect();
307 _modified_connection.disconnect();
308 _commit_connection.disconnect();
309 _reconstruction_start_connection.disconnect();
310 _reconstruction_finish_connection.disconnect();
312 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
313 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
314 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
316 while (event_context) {
317 SPEventContext *ec = event_context;
318 event_context = ec->next;
319 sp_event_context_finish (ec);
320 g_object_unref (G_OBJECT (ec));
321 }
323 if (_layer_hierarchy) {
324 delete _layer_hierarchy;
325 }
327 if (_inkscape) {
328 _inkscape = NULL;
329 }
331 if (drawing) {
332 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
333 drawing = NULL;
334 }
336 delete _guides_message_context;
337 _guides_message_context = NULL;
339 g_list_free (zooms_past);
340 g_list_free (zooms_future);
341 }
343 SPDesktop::~SPDesktop() {}
345 //--------------------------------------------------------------------
346 /* Public methods */
348 void SPDesktop::setDisplayModeNormal()
349 {
350 prefs_set_int_attribute("options.outlinemode", "value", 0);
351 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
352 canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
353 displayMode = RENDERMODE_NORMAL;
354 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
355 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
356 }
358 void SPDesktop::setDisplayModeOutline()
359 {
360 prefs_set_int_attribute("options.outlinemode", "value", 1);
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 (prefs_get_int_attribute("options.outlinemode", "value", prefs_get_int_attribute("options.startmode", "outline", 0)))
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::Rect const bbox = sp_item_bbox_desktop(item);
434 return viewport.contains(bbox);
435 }
437 ///
438 bool SPDesktop::itemIsHidden(SPItem const *item) const {
439 return item->isHidden(this->dkey);
440 }
442 /**
443 * Set activate property of desktop; emit signal if changed.
444 */
445 void
446 SPDesktop::set_active (bool new_active)
447 {
448 if (new_active != _active) {
449 _active = new_active;
450 if (new_active) {
451 _activate_signal.emit();
452 } else {
453 _deactivate_signal.emit();
454 }
455 }
456 }
458 /**
459 * Set activate status of current desktop's named view.
460 */
461 void
462 SPDesktop::activate_guides(bool activate)
463 {
464 guides_active = activate;
465 namedview->activateGuides(this, activate);
466 }
468 /**
469 * Make desktop switch documents.
470 */
471 void
472 SPDesktop::change_document (SPDocument *theDocument)
473 {
474 g_return_if_fail (theDocument != NULL);
476 /* unselect everything before switching documents */
477 selection->clear();
479 setDocument (theDocument);
480 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
481 _document_replaced_signal.emit (this, theDocument);
482 }
484 /**
485 * Make desktop switch event contexts.
486 */
487 void
488 SPDesktop::set_event_context (GtkType type, const gchar *config)
489 {
490 SPEventContext *ec;
491 while (event_context) {
492 ec = event_context;
493 sp_event_context_deactivate (ec);
494 event_context = ec->next;
495 sp_event_context_finish (ec);
496 g_object_unref (G_OBJECT (ec));
497 }
499 Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
500 ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
501 ec->next = event_context;
502 event_context = ec;
503 sp_event_context_activate (ec);
504 _event_context_changed_signal.emit (this, ec);
505 }
507 /**
508 * Push event context onto desktop's context stack.
509 */
510 void
511 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
512 {
513 SPEventContext *ref, *ec;
514 Inkscape::XML::Node *repr;
516 if (event_context && event_context->key == key) return;
517 ref = event_context;
518 while (ref && ref->next && ref->next->key != key) ref = ref->next;
519 if (ref && ref->next) {
520 ec = ref->next;
521 ref->next = ec->next;
522 sp_event_context_finish (ec);
523 g_object_unref (G_OBJECT (ec));
524 }
526 if (event_context) sp_event_context_deactivate (event_context);
527 repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
528 ec = sp_event_context_new (type, this, repr, key);
529 ec->next = event_context;
530 event_context = ec;
531 sp_event_context_activate (ec);
532 _event_context_changed_signal.emit (this, ec);
533 }
535 /**
536 * Sets the coordinate status to a given point
537 */
538 void
539 SPDesktop::set_coordinate_status (NR::Point p) {
540 _widget->setCoordinateStatus(p);
541 }
543 /**
544 * \see sp_document_item_from_list_at_point_bottom()
545 */
546 SPItem *
547 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
548 {
549 g_return_val_if_fail (doc() != NULL, NULL);
550 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
551 }
553 /**
554 * \see sp_document_item_at_point()
555 */
556 SPItem *
557 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
558 {
559 g_return_val_if_fail (doc() != NULL, NULL);
560 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
561 }
563 /**
564 * \see sp_document_group_at_point()
565 */
566 SPItem *
567 SPDesktop::group_at_point (NR::Point const p) const
568 {
569 g_return_val_if_fail (doc() != NULL, NULL);
570 return sp_document_group_at_point (doc(), dkey, p);
571 }
573 /**
574 * \brief Returns the mouse point in document coordinates; if mouse is
575 * outside the canvas, returns the center of canvas viewpoint
576 */
577 NR::Point
578 SPDesktop::point() const
579 {
580 NR::Point p = _widget->getPointer();
581 NR::Point pw = sp_canvas_window_to_world (canvas, p);
582 p = w2d(pw);
584 NR::Rect const r = canvas->getViewbox();
586 NR::Point r0 = w2d(r.min());
587 NR::Point r1 = w2d(r.max());
589 if (p[NR::X] >= r0[NR::X] &&
590 p[NR::X] <= r1[NR::X] &&
591 p[NR::Y] >= r1[NR::Y] &&
592 p[NR::Y] <= r0[NR::Y])
593 {
594 return p;
595 } else {
596 return (r0 + r1) / 2;
597 }
598 }
600 /**
601 * Put current zoom data in history list.
602 */
603 void
604 SPDesktop::push_current_zoom (GList **history)
605 {
606 NR::Rect const area = get_display_area();
608 NRRect *old_zoom = g_new(NRRect, 1);
609 old_zoom->x0 = area.min()[NR::X];
610 old_zoom->x1 = area.max()[NR::X];
611 old_zoom->y0 = area.min()[NR::Y];
612 old_zoom->y1 = area.max()[NR::Y];
613 if ( *history == NULL
614 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
615 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
616 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
617 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
618 {
619 *history = g_list_prepend (*history, old_zoom);
620 }
621 }
623 /**
624 * Set viewbox.
625 */
626 void
627 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
628 {
629 g_assert(_widget);
631 // save the zoom
632 if (log) {
633 push_current_zoom(&zooms_past);
634 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
635 g_list_free (zooms_future);
636 zooms_future = NULL;
637 }
639 double const cx = 0.5 * (x0 + x1);
640 double const cy = 0.5 * (y0 + y1);
642 NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
644 double scale = expansion(_d2w);
645 double newscale;
646 if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
647 newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
648 } else {
649 newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
650 }
652 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
654 int clear = FALSE;
655 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
656 /* Set zoom factors */
657 _d2w = NR::Matrix(NR::scale(newscale, -newscale));
658 _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
659 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
660 clear = TRUE;
661 }
663 /* Calculate top left corner */
664 x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
665 y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
667 /* Scroll */
668 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
670 _widget->updateRulers();
671 _widget->updateScrollbars(expansion(_d2w));
672 _widget->updateZoom();
673 }
675 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
676 {
677 set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
678 }
680 /**
681 * Return viewbox dimensions.
682 */
683 NR::Rect SPDesktop::get_display_area() const
684 {
685 NR::Rect const viewbox = canvas->getViewbox();
687 double const scale = _d2w[0];
689 return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
690 NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
691 }
693 /**
694 * Revert back to previous zoom if possible.
695 */
696 void
697 SPDesktop::prev_zoom()
698 {
699 if (zooms_past == NULL) {
700 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
701 return;
702 }
704 // push current zoom into forward zooms list
705 push_current_zoom (&zooms_future);
707 // restore previous zoom
708 set_display_area (((NRRect *) zooms_past->data)->x0,
709 ((NRRect *) zooms_past->data)->y0,
710 ((NRRect *) zooms_past->data)->x1,
711 ((NRRect *) zooms_past->data)->y1,
712 0, false);
714 // remove the just-added zoom from the past zooms list
715 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
716 }
718 /**
719 * Set zoom to next in list.
720 */
721 void
722 SPDesktop::next_zoom()
723 {
724 if (zooms_future == NULL) {
725 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
726 return;
727 }
729 // push current zoom into past zooms list
730 push_current_zoom (&zooms_past);
732 // restore next zoom
733 set_display_area (((NRRect *) zooms_future->data)->x0,
734 ((NRRect *) zooms_future->data)->y0,
735 ((NRRect *) zooms_future->data)->x1,
736 ((NRRect *) zooms_future->data)->y1,
737 0, false);
739 // remove the just-used zoom from the zooms_future list
740 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
741 }
743 /**
744 * Zoom to point with absolute zoom factor.
745 */
746 void
747 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
748 {
749 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
751 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
752 // this check prevents "sliding" when trying to zoom in at maximum zoom;
753 /// \todo someone please fix calculations properly and remove this hack
754 if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
755 return;
757 NR::Rect const viewbox = canvas->getViewbox();
759 double const width2 = viewbox.dimensions()[NR::X] / zoom;
760 double const height2 = viewbox.dimensions()[NR::Y] / zoom;
762 set_display_area(cx - px * width2,
763 cy - py * height2,
764 cx + (1 - px) * width2,
765 cy + (1 - py) * height2,
766 0.0);
767 }
769 /**
770 * Zoom to center with absolute zoom factor.
771 */
772 void
773 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
774 {
775 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
776 }
778 /**
779 * Zoom to point with relative zoom factor.
780 */
781 void
782 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
783 {
784 NR::Rect const area = get_display_area();
786 if (cx < area.min()[NR::X]) {
787 cx = area.min()[NR::X];
788 }
789 if (cx > area.max()[NR::X]) {
790 cx = area.max()[NR::X];
791 }
792 if (cy < area.min()[NR::Y]) {
793 cy = area.min()[NR::Y];
794 }
795 if (cy > area.max()[NR::Y]) {
796 cy = area.max()[NR::Y];
797 }
799 gdouble const scale = expansion(_d2w) * zoom;
800 double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
801 double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
803 zoom_absolute_keep_point(cx, cy, px, py, scale);
804 }
806 /**
807 * Zoom to center with relative zoom factor.
808 */
809 void
810 SPDesktop::zoom_relative (double cx, double cy, double zoom)
811 {
812 gdouble scale = expansion(_d2w) * zoom;
813 zoom_absolute (cx, cy, scale);
814 }
816 /**
817 * Set display area to origin and current document dimensions.
818 */
819 void
820 SPDesktop::zoom_page()
821 {
822 NR::Rect d(NR::Point(0, 0),
823 NR::Point(sp_document_width(doc()), sp_document_height(doc())));
825 if (d.dimensions()[NR::X] < 1.0 || d.dimensions()[NR::Y] < 1.0) {
826 return;
827 }
829 set_display_area(d, 10);
830 }
832 /**
833 * Set display area to current document width.
834 */
835 void
836 SPDesktop::zoom_page_width()
837 {
838 NR::Rect const a = get_display_area();
840 if (sp_document_width(doc()) < 1.0) {
841 return;
842 }
844 NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
845 NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
847 set_display_area(d, 10);
848 }
850 /**
851 * Zoom to selection.
852 */
853 void
854 SPDesktop::zoom_selection()
855 {
856 NR::Rect const d = selection->bounds();
858 if (d.dimensions()[NR::X] < 0.1 || d.dimensions()[NR::Y] < 0.1) {
859 return;
860 }
862 set_display_area(d, 10);
863 }
865 /**
866 * Tell widget to let zoom widget grab keyboard focus.
867 */
868 void
869 SPDesktop::zoom_grab_focus()
870 {
871 _widget->letZoomGrabFocus();
872 }
874 /**
875 * Zoom to whole drawing.
876 */
877 void
878 SPDesktop::zoom_drawing()
879 {
880 g_return_if_fail (doc() != NULL);
881 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
882 g_return_if_fail (docitem != NULL);
884 NR::Rect d = sp_item_bbox_desktop(docitem);
886 /* Note that the second condition here indicates that
887 ** there are no items in the drawing.
888 */
889 if ( d.dimensions()[NR::X] < 1.0 || d.dimensions()[NR::Y] < 1.0 ) {
890 return;
891 }
893 set_display_area(d, 10);
894 }
896 /**
897 * Scroll canvas by specific coordinate amount.
898 */
899 void
900 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
901 {
902 g_assert(_widget);
904 NR::Rect const viewbox = canvas->getViewbox();
906 sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
908 _widget->updateRulers();
909 _widget->updateScrollbars(expansion(_d2w));
910 }
912 bool
913 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
914 {
915 gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
917 // autoscrolldistance is in screen pixels, but the display area is in document units
918 autoscrolldistance /= expansion(_d2w);
919 NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
921 if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
922 !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y]) ) {
924 NR::Point const s_w( (*p) * _d2w );
926 gdouble x_to;
927 if ((*p)[NR::X] < dbox.min()[NR::X])
928 x_to = dbox.min()[NR::X];
929 else if ((*p)[NR::X] > dbox.max()[NR::X])
930 x_to = dbox.max()[NR::X];
931 else
932 x_to = (*p)[NR::X];
934 gdouble y_to;
935 if ((*p)[NR::Y] < dbox.min()[NR::Y])
936 y_to = dbox.min()[NR::Y];
937 else if ((*p)[NR::Y] > dbox.max()[NR::Y])
938 y_to = dbox.max()[NR::Y];
939 else
940 y_to = (*p)[NR::Y];
942 NR::Point const d_dt(x_to, y_to);
943 NR::Point const d_w( d_dt * _d2w );
944 NR::Point const moved_w( d_w - s_w );
946 if (autoscrollspeed == 0)
947 autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
949 if (autoscrollspeed != 0)
950 scroll_world (autoscrollspeed * moved_w);
952 return true;
953 }
954 return false;
955 }
957 void
958 SPDesktop::fullscreen()
959 {
960 _widget->setFullscreen();
961 }
963 void
964 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
965 {
966 _widget->getGeometry (x, y, w, h);
967 }
969 void
970 SPDesktop::setWindowPosition (NR::Point p)
971 {
972 _widget->setPosition (p);
973 }
975 void
976 SPDesktop::setWindowSize (gint w, gint h)
977 {
978 _widget->setSize (w, h);
979 }
981 void
982 SPDesktop::setWindowTransient (void *p, int transient_policy)
983 {
984 _widget->setTransient (p, transient_policy);
985 }
987 void
988 SPDesktop::presentWindow()
989 {
990 _widget->present();
991 }
993 bool
994 SPDesktop::warnDialog (gchar *text)
995 {
996 return _widget->warnDialog (text);
997 }
999 void
1000 SPDesktop::toggleRulers()
1001 {
1002 _widget->toggleRulers();
1003 }
1005 void
1006 SPDesktop::toggleScrollbars()
1007 {
1008 _widget->toggleScrollbars();
1009 }
1011 void
1012 SPDesktop::layoutWidget()
1013 {
1014 _widget->layout();
1015 }
1017 void
1018 SPDesktop::destroyWidget()
1019 {
1020 _widget->destroy();
1021 }
1023 bool
1024 SPDesktop::shutdown()
1025 {
1026 return _widget->shutdown();
1027 }
1029 void
1030 SPDesktop::setToolboxFocusTo (gchar const *label)
1031 {
1032 _widget->setToolboxFocusTo (label);
1033 }
1035 void
1036 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1037 {
1038 _widget->setToolboxAdjustmentValue (id, val);
1039 }
1041 bool
1042 SPDesktop::isToolboxButtonActive (gchar const *id)
1043 {
1044 return _widget->isToolboxButtonActive (id);
1045 }
1047 void
1048 SPDesktop::emitToolSubselectionChanged(gpointer data)
1049 {
1050 _tool_subselection_changed.emit(data);
1051 inkscape_subselection_changed (this);
1052 }
1054 void
1055 SPDesktop::updateNow()
1056 {
1057 sp_canvas_update_now(canvas);
1058 }
1060 void
1061 SPDesktop::enableInteraction()
1062 {
1063 _widget->enableInteraction();
1064 }
1066 void SPDesktop::disableInteraction()
1067 {
1068 _widget->disableInteraction();
1069 }
1071 //----------------------------------------------------------------------
1072 // Callback implementations. The virtual ones are connected by the view.
1074 void
1075 SPDesktop::onPositionSet (double x, double y)
1076 {
1077 _widget->viewSetPosition (NR::Point(x,y));
1078 }
1080 void
1081 SPDesktop::onResized (double x, double y)
1082 {
1083 // Nothing called here
1084 }
1086 /**
1087 * Redraw callback; queues Gtk redraw; connected by View.
1088 */
1089 void
1090 SPDesktop::onRedrawRequested ()
1091 {
1092 if (main) {
1093 _widget->requestCanvasUpdate();
1094 }
1095 }
1097 void
1098 SPDesktop::updateCanvasNow()
1099 {
1100 _widget->requestCanvasUpdateAndWait();
1101 }
1103 /**
1104 * Associate document with desktop.
1105 */
1106 /// \todo fixme: refactor SPDesktop::init to use setDocument
1107 void
1108 SPDesktop::setDocument (SPDocument *doc)
1109 {
1110 if (this->doc() && doc) {
1111 namedview->hide(this);
1112 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1113 }
1115 if (_layer_hierarchy) {
1116 _layer_hierarchy->clear();
1117 delete _layer_hierarchy;
1118 }
1119 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1120 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1121 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1122 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1123 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1125 /* setup EventLog */
1126 event_log = new Inkscape::EventLog(doc);
1127 doc->addUndoObserver(*event_log);
1129 _commit_connection.disconnect();
1130 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1132 /// \todo fixme: This condition exists to make sure the code
1133 /// inside is called only once on initialization. But there
1134 /// are surely more safe methods to accomplish this.
1135 if (drawing) {
1136 NRArenaItem *ai;
1138 namedview = sp_document_namedview (doc, NULL);
1139 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1140 number = namedview->getViewCount();
1142 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1143 SP_CANVAS_ARENA (drawing)->arena,
1144 dkey,
1145 SP_ITEM_SHOW_DISPLAY);
1146 if (ai) {
1147 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1148 nr_arena_item_unref (ai);
1149 }
1150 namedview->show(this);
1151 /* Ugly hack */
1152 activate_guides (true);
1153 /* Ugly hack */
1154 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1155 }
1157 _document_replaced_signal.emit (this, doc);
1159 View::setDocument (doc);
1160 }
1162 void
1163 SPDesktop::onStatusMessage
1164 (Inkscape::MessageType type, gchar const *message)
1165 {
1166 if (_widget) {
1167 _widget->setMessage(type, message);
1168 }
1169 }
1171 void
1172 SPDesktop::onDocumentURISet (gchar const* uri)
1173 {
1174 _widget->setTitle(uri);
1175 }
1177 /**
1178 * Resized callback.
1179 */
1180 void
1181 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1182 {
1183 _doc2dt[5] = height;
1184 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1185 NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1186 SP_CTRLRECT(page)->setRectangle(a);
1187 SP_CTRLRECT(page_border)->setRectangle(a);
1188 }
1191 void
1192 SPDesktop::_onActivate (SPDesktop* dt)
1193 {
1194 if (!dt->_widget) return;
1195 dt->_widget->activateDesktop();
1196 }
1198 void
1199 SPDesktop::_onDeactivate (SPDesktop* dt)
1200 {
1201 if (!dt->_widget) return;
1202 dt->_widget->deactivateDesktop();
1203 }
1205 void
1206 SPDesktop::_onSelectionModified
1207 (Inkscape::Selection *selection, guint flags, SPDesktop *dt)
1208 {
1209 if (!dt->_widget) return;
1210 dt->_widget->updateScrollbars (expansion(dt->_d2w));
1211 }
1213 static void
1214 _onSelectionChanged
1215 (Inkscape::Selection *selection, SPDesktop *desktop)
1216 {
1217 /** \todo
1218 * only change the layer for single selections, or what?
1219 * This seems reasonable -- for multiple selections there can be many
1220 * different layers involved.
1221 */
1222 SPItem *item=selection->singleItem();
1223 if (item) {
1224 SPObject *layer=desktop->layerForObject(item);
1225 if ( layer && layer != desktop->currentLayer() ) {
1226 desktop->setCurrentLayer(layer);
1227 }
1228 }
1229 }
1231 /**
1232 * Calls event handler of current event context.
1233 * \param arena Unused
1234 * \todo fixme
1235 */
1236 static gint
1237 _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1238 {
1239 if (ai) {
1240 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1241 return sp_event_context_item_handler (desktop->event_context, spi, event);
1242 } else {
1243 return sp_event_context_root_handler (desktop->event_context, event);
1244 }
1245 }
1247 static void
1248 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1249 g_return_if_fail(SP_IS_GROUP(layer));
1250 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1251 }
1253 /// Callback
1254 static void
1255 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1256 g_return_if_fail(SP_IS_GROUP(layer));
1257 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1258 }
1260 /// Callback
1261 static void
1262 _layer_hierarchy_changed(SPObject *top, SPObject *bottom,
1263 SPDesktop *desktop)
1264 {
1265 desktop->_layer_changed_signal.emit (bottom);
1266 }
1268 /// Called when document is starting to be rebuilt.
1269 static void
1270 _reconstruction_start (SPDesktop * desktop)
1271 {
1272 // printf("Desktop, starting reconstruction\n");
1273 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1274 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1276 /*
1277 GSList const * selection_objs = desktop->selection->list();
1278 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1280 }
1281 */
1282 desktop->selection->clear();
1284 // printf("Desktop, starting reconstruction end\n");
1285 }
1287 /// Called when document rebuild is finished.
1288 static void
1289 _reconstruction_finish (SPDesktop * desktop)
1290 {
1291 // printf("Desktop, finishing reconstruction\n");
1292 if (desktop->_reconstruction_old_layer_id == NULL)
1293 return;
1295 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1296 if (newLayer != NULL)
1297 desktop->setCurrentLayer(newLayer);
1299 g_free(desktop->_reconstruction_old_layer_id);
1300 desktop->_reconstruction_old_layer_id = NULL;
1301 // printf("Desktop, finishing reconstruction end\n");
1302 return;
1303 }
1305 /**
1306 * Namedview_modified callback.
1307 */
1308 static void
1309 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1310 {
1311 SPNamedView *nv=SP_NAMEDVIEW(obj);
1313 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1315 /* Recalculate snap distances */
1316 /* FIXME: why is the desktop getting involved in setting up something
1317 ** that is entirely to do with the namedview?
1318 */
1319 _update_snap_distances (desktop);
1321 /* Show/hide page background */
1322 if (nv->pagecolor & 0xff) {
1323 sp_canvas_item_show (desktop->table);
1324 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1325 sp_canvas_item_move_to_z (desktop->table, 0);
1326 } else {
1327 sp_canvas_item_hide (desktop->table);
1328 }
1330 /* Show/hide page border */
1331 if (nv->showborder) {
1332 // show
1333 sp_canvas_item_show (desktop->page_border);
1334 // set color and shadow
1335 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1336 if (nv->pageshadow) {
1337 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1338 }
1339 // place in the z-order stack
1340 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1341 sp_canvas_item_move_to_z (desktop->page_border, 2);
1342 } else {
1343 int order = sp_canvas_item_order (desktop->page_border);
1344 int morder = sp_canvas_item_order (desktop->drawing);
1345 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1346 morder - order);
1347 }
1348 } else {
1349 sp_canvas_item_hide (desktop->page_border);
1350 if (nv->pageshadow) {
1351 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1352 }
1353 }
1355 /* Show/hide page shadow */
1356 if (nv->showpageshadow && nv->pageshadow) {
1357 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1358 } else {
1359 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1360 }
1362 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1363 (SP_RGBA32_R_U(nv->pagecolor) +
1364 SP_RGBA32_G_U(nv->pagecolor) +
1365 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1366 // the background color is light or transparent, use black outline
1367 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1368 } else { // use white outline
1369 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1370 }
1371 }
1372 }
1374 /**
1375 * Callback to reset snapper's distances.
1376 */
1377 static void
1378 _update_snap_distances (SPDesktop *desktop)
1379 {
1380 SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1382 SPNamedView &nv = *desktop->namedview;
1384 nv.snap_manager.grid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1385 *nv.gridtoleranceunit,
1386 px));
1387 nv.snap_manager.axonomgrid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1388 *nv.gridtoleranceunit,
1389 px));
1390 nv.snap_manager.guide.setDistance(sp_convert_distance_full(nv.guidetolerance,
1391 *nv.guidetoleranceunit,
1392 px));
1393 nv.snap_manager.object.setDistance(sp_convert_distance_full(nv.objecttolerance,
1394 *nv.objecttoleranceunit,
1395 px));
1396 }
1399 NR::Matrix SPDesktop::w2d() const
1400 {
1401 return _w2d;
1402 }
1404 NR::Point SPDesktop::w2d(NR::Point const &p) const
1405 {
1406 return p * _w2d;
1407 }
1409 NR::Point SPDesktop::d2w(NR::Point const &p) const
1410 {
1411 return p * _d2w;
1412 }
1414 NR::Matrix SPDesktop::doc2dt() const
1415 {
1416 return _doc2dt;
1417 }
1419 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1420 {
1421 return p * _doc2dt;
1422 }
1424 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1425 {
1426 return p / _doc2dt;
1427 }
1430 /**
1431 * Pop event context from desktop's context stack. Never used.
1432 */
1433 // void
1434 // SPDesktop::pop_event_context (unsigned int key)
1435 // {
1436 // SPEventContext *ec = NULL;
1437 //
1438 // if (event_context && event_context->key == key) {
1439 // g_return_if_fail (event_context);
1440 // g_return_if_fail (event_context->next);
1441 // ec = event_context;
1442 // sp_event_context_deactivate (ec);
1443 // event_context = ec->next;
1444 // sp_event_context_activate (event_context);
1445 // _event_context_changed_signal.emit (this, ec);
1446 // }
1447 //
1448 // SPEventContext *ref = event_context;
1449 // while (ref && ref->next && ref->next->key != key)
1450 // ref = ref->next;
1451 //
1452 // if (ref && ref->next) {
1453 // ec = ref->next;
1454 // ref->next = ec->next;
1455 // }
1456 //
1457 // if (ec) {
1458 // sp_event_context_finish (ec);
1459 // g_object_unref (G_OBJECT (ec));
1460 // }
1461 // }
1463 /*
1464 Local Variables:
1465 mode:c++
1466 c-file-style:"stroustrup"
1467 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1468 indent-tabs-mode:nil
1469 fill-column:99
1470 End:
1471 */
1472 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :