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 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
351 canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
352 displayMode = RENDERMODE_NORMAL;
353 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
354 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
355 }
357 void SPDesktop::setDisplayModeOutline()
358 {
359 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
360 canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
361 displayMode = RENDERMODE_OUTLINE;
362 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
363 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
364 }
366 void SPDesktop::displayModeToggle()
367 {
368 if (displayMode == RENDERMODE_OUTLINE)
369 setDisplayModeNormal();
370 else
371 setDisplayModeOutline();
372 }
374 /**
375 * Returns current root (=bottom) layer.
376 */
377 SPObject *SPDesktop::currentRoot() const
378 {
379 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
380 }
382 /**
383 * Returns current top layer.
384 */
385 SPObject *SPDesktop::currentLayer() const
386 {
387 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
388 }
390 /**
391 * Sets the current layer of the desktop.
392 *
393 * Make \a object the top layer.
394 */
395 void SPDesktop::setCurrentLayer(SPObject *object) {
396 g_return_if_fail(SP_IS_GROUP(object));
397 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
398 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
399 _layer_hierarchy->setBottom(object);
400 }
402 /**
403 * Return layer that contains \a object.
404 */
405 SPObject *SPDesktop::layerForObject(SPObject *object) {
406 g_return_val_if_fail(object != NULL, NULL);
408 SPObject *root=currentRoot();
409 object = SP_OBJECT_PARENT(object);
410 while ( object && object != root && !isLayer(object) ) {
411 object = SP_OBJECT_PARENT(object);
412 }
413 return object;
414 }
416 /**
417 * True if object is a layer.
418 */
419 bool SPDesktop::isLayer(SPObject *object) const {
420 return ( SP_IS_GROUP(object)
421 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
422 == SPGroup::LAYER ) );
423 }
425 /**
426 * True if desktop viewport fully contains \a item's bbox.
427 */
428 bool SPDesktop::isWithinViewport (SPItem *item) const
429 {
430 NR::Rect const viewport = get_display_area();
431 NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
432 if (bbox) {
433 return viewport.contains(*bbox);
434 } else {
435 return true;
436 }
437 }
439 ///
440 bool SPDesktop::itemIsHidden(SPItem const *item) const {
441 return item->isHidden(this->dkey);
442 }
444 /**
445 * Set activate property of desktop; emit signal if changed.
446 */
447 void
448 SPDesktop::set_active (bool new_active)
449 {
450 if (new_active != _active) {
451 _active = new_active;
452 if (new_active) {
453 _activate_signal.emit();
454 } else {
455 _deactivate_signal.emit();
456 }
457 }
458 }
460 /**
461 * Set activate status of current desktop's named view.
462 */
463 void
464 SPDesktop::activate_guides(bool activate)
465 {
466 guides_active = activate;
467 namedview->activateGuides(this, activate);
468 }
470 /**
471 * Make desktop switch documents.
472 */
473 void
474 SPDesktop::change_document (SPDocument *theDocument)
475 {
476 g_return_if_fail (theDocument != NULL);
478 /* unselect everything before switching documents */
479 selection->clear();
481 setDocument (theDocument);
482 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
483 _document_replaced_signal.emit (this, theDocument);
484 }
486 /**
487 * Make desktop switch event contexts.
488 */
489 void
490 SPDesktop::set_event_context (GtkType type, const gchar *config)
491 {
492 SPEventContext *ec;
493 while (event_context) {
494 ec = event_context;
495 sp_event_context_deactivate (ec);
496 event_context = ec->next;
497 sp_event_context_finish (ec);
498 g_object_unref (G_OBJECT (ec));
499 }
501 Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
502 ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
503 ec->next = event_context;
504 event_context = ec;
505 sp_event_context_activate (ec);
506 _event_context_changed_signal.emit (this, ec);
507 }
509 /**
510 * Push event context onto desktop's context stack.
511 */
512 void
513 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
514 {
515 SPEventContext *ref, *ec;
516 Inkscape::XML::Node *repr;
518 if (event_context && event_context->key == key) return;
519 ref = event_context;
520 while (ref && ref->next && ref->next->key != key) ref = ref->next;
521 if (ref && ref->next) {
522 ec = ref->next;
523 ref->next = ec->next;
524 sp_event_context_finish (ec);
525 g_object_unref (G_OBJECT (ec));
526 }
528 if (event_context) sp_event_context_deactivate (event_context);
529 repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
530 ec = sp_event_context_new (type, this, repr, key);
531 ec->next = event_context;
532 event_context = ec;
533 sp_event_context_activate (ec);
534 _event_context_changed_signal.emit (this, ec);
535 }
537 /**
538 * Sets the coordinate status to a given point
539 */
540 void
541 SPDesktop::set_coordinate_status (NR::Point p) {
542 _widget->setCoordinateStatus(p);
543 }
545 /**
546 * \see sp_document_item_from_list_at_point_bottom()
547 */
548 SPItem *
549 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
550 {
551 g_return_val_if_fail (doc() != NULL, NULL);
552 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
553 }
555 /**
556 * \see sp_document_item_at_point()
557 */
558 SPItem *
559 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
560 {
561 g_return_val_if_fail (doc() != NULL, NULL);
562 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
563 }
565 /**
566 * \see sp_document_group_at_point()
567 */
568 SPItem *
569 SPDesktop::group_at_point (NR::Point const p) const
570 {
571 g_return_val_if_fail (doc() != NULL, NULL);
572 return sp_document_group_at_point (doc(), dkey, p);
573 }
575 /**
576 * \brief Returns the mouse point in document coordinates; if mouse is
577 * outside the canvas, returns the center of canvas viewpoint
578 */
579 NR::Point
580 SPDesktop::point() const
581 {
582 NR::Point p = _widget->getPointer();
583 NR::Point pw = sp_canvas_window_to_world (canvas, p);
584 p = w2d(pw);
586 NR::Rect const r = canvas->getViewbox();
588 NR::Point r0 = w2d(r.min());
589 NR::Point r1 = w2d(r.max());
591 if (p[NR::X] >= r0[NR::X] &&
592 p[NR::X] <= r1[NR::X] &&
593 p[NR::Y] >= r1[NR::Y] &&
594 p[NR::Y] <= r0[NR::Y])
595 {
596 return p;
597 } else {
598 return (r0 + r1) / 2;
599 }
600 }
602 /**
603 * Put current zoom data in history list.
604 */
605 void
606 SPDesktop::push_current_zoom (GList **history)
607 {
608 NR::Rect const area = get_display_area();
610 NRRect *old_zoom = g_new(NRRect, 1);
611 old_zoom->x0 = area.min()[NR::X];
612 old_zoom->x1 = area.max()[NR::X];
613 old_zoom->y0 = area.min()[NR::Y];
614 old_zoom->y1 = area.max()[NR::Y];
615 if ( *history == NULL
616 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
617 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
618 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
619 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
620 {
621 *history = g_list_prepend (*history, old_zoom);
622 }
623 }
625 /**
626 * Set viewbox.
627 */
628 void
629 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
630 {
631 g_assert(_widget);
633 // save the zoom
634 if (log) {
635 push_current_zoom(&zooms_past);
636 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
637 g_list_free (zooms_future);
638 zooms_future = NULL;
639 }
641 double const cx = 0.5 * (x0 + x1);
642 double const cy = 0.5 * (y0 + y1);
644 NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
646 double scale = expansion(_d2w);
647 double newscale;
648 if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
649 newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
650 } else {
651 newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
652 }
654 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
656 int clear = FALSE;
657 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
658 /* Set zoom factors */
659 _d2w = NR::Matrix(NR::scale(newscale, -newscale));
660 _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
661 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
662 clear = TRUE;
663 }
665 /* Calculate top left corner */
666 x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
667 y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
669 /* Scroll */
670 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
672 _widget->updateRulers();
673 _widget->updateScrollbars(expansion(_d2w));
674 _widget->updateZoom();
675 }
677 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
678 {
679 set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
680 }
682 /**
683 * Return viewbox dimensions.
684 */
685 NR::Rect SPDesktop::get_display_area() const
686 {
687 NR::Rect const viewbox = canvas->getViewbox();
689 double const scale = _d2w[0];
691 return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
692 NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
693 }
695 /**
696 * Revert back to previous zoom if possible.
697 */
698 void
699 SPDesktop::prev_zoom()
700 {
701 if (zooms_past == NULL) {
702 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
703 return;
704 }
706 // push current zoom into forward zooms list
707 push_current_zoom (&zooms_future);
709 // restore previous zoom
710 set_display_area (((NRRect *) zooms_past->data)->x0,
711 ((NRRect *) zooms_past->data)->y0,
712 ((NRRect *) zooms_past->data)->x1,
713 ((NRRect *) zooms_past->data)->y1,
714 0, false);
716 // remove the just-added zoom from the past zooms list
717 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
718 }
720 /**
721 * Set zoom to next in list.
722 */
723 void
724 SPDesktop::next_zoom()
725 {
726 if (zooms_future == NULL) {
727 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
728 return;
729 }
731 // push current zoom into past zooms list
732 push_current_zoom (&zooms_past);
734 // restore next zoom
735 set_display_area (((NRRect *) zooms_future->data)->x0,
736 ((NRRect *) zooms_future->data)->y0,
737 ((NRRect *) zooms_future->data)->x1,
738 ((NRRect *) zooms_future->data)->y1,
739 0, false);
741 // remove the just-used zoom from the zooms_future list
742 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
743 }
745 /**
746 * Zoom to point with absolute zoom factor.
747 */
748 void
749 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
750 {
751 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
753 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
754 // this check prevents "sliding" when trying to zoom in at maximum zoom;
755 /// \todo someone please fix calculations properly and remove this hack
756 if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
757 return;
759 NR::Rect const viewbox = canvas->getViewbox();
761 double const width2 = viewbox.dimensions()[NR::X] / zoom;
762 double const height2 = viewbox.dimensions()[NR::Y] / zoom;
764 set_display_area(cx - px * width2,
765 cy - py * height2,
766 cx + (1 - px) * width2,
767 cy + (1 - py) * height2,
768 0.0);
769 }
771 /**
772 * Zoom to center with absolute zoom factor.
773 */
774 void
775 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
776 {
777 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
778 }
780 /**
781 * Zoom to point with relative zoom factor.
782 */
783 void
784 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
785 {
786 NR::Rect const area = get_display_area();
788 if (cx < area.min()[NR::X]) {
789 cx = area.min()[NR::X];
790 }
791 if (cx > area.max()[NR::X]) {
792 cx = area.max()[NR::X];
793 }
794 if (cy < area.min()[NR::Y]) {
795 cy = area.min()[NR::Y];
796 }
797 if (cy > area.max()[NR::Y]) {
798 cy = area.max()[NR::Y];
799 }
801 gdouble const scale = expansion(_d2w) * zoom;
802 double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
803 double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
805 zoom_absolute_keep_point(cx, cy, px, py, scale);
806 }
808 /**
809 * Zoom to center with relative zoom factor.
810 */
811 void
812 SPDesktop::zoom_relative (double cx, double cy, double zoom)
813 {
814 gdouble scale = expansion(_d2w) * zoom;
815 zoom_absolute (cx, cy, scale);
816 }
818 /**
819 * Set display area to origin and current document dimensions.
820 */
821 void
822 SPDesktop::zoom_page()
823 {
824 NR::Rect d(NR::Point(0, 0),
825 NR::Point(sp_document_width(doc()), sp_document_height(doc())));
827 if (d.dimensions()[NR::X] < 1.0 || d.dimensions()[NR::Y] < 1.0) {
828 return;
829 }
831 set_display_area(d, 10);
832 }
834 /**
835 * Set display area to current document width.
836 */
837 void
838 SPDesktop::zoom_page_width()
839 {
840 NR::Rect const a = get_display_area();
842 if (sp_document_width(doc()) < 1.0) {
843 return;
844 }
846 NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
847 NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
849 set_display_area(d, 10);
850 }
852 /**
853 * Zoom to selection.
854 */
855 void
856 SPDesktop::zoom_selection()
857 {
858 NR::Maybe<NR::Rect> const d = selection->bounds();
860 if ( !d || d->dimensions()[NR::X] < 0.1 || d->dimensions()[NR::Y] < 0.1) {
861 return;
862 }
864 set_display_area(*d, 10);
865 }
867 /**
868 * Tell widget to let zoom widget grab keyboard focus.
869 */
870 void
871 SPDesktop::zoom_grab_focus()
872 {
873 _widget->letZoomGrabFocus();
874 }
876 /**
877 * Zoom to whole drawing.
878 */
879 void
880 SPDesktop::zoom_drawing()
881 {
882 g_return_if_fail (doc() != NULL);
883 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
884 g_return_if_fail (docitem != NULL);
886 NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
888 /* Note that the second condition here indicates that
889 ** there are no items in the drawing.
890 */
891 if ( !d || d->dimensions()[NR::X] < 1.0 || d->dimensions()[NR::Y] < 1.0 ) {
892 return;
893 }
895 set_display_area(*d, 10);
896 }
898 /**
899 * Scroll canvas by specific coordinate amount.
900 */
901 void
902 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
903 {
904 g_assert(_widget);
906 NR::Rect const viewbox = canvas->getViewbox();
908 sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
910 _widget->updateRulers();
911 _widget->updateScrollbars(expansion(_d2w));
912 }
914 bool
915 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
916 {
917 gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
919 // autoscrolldistance is in screen pixels, but the display area is in document units
920 autoscrolldistance /= expansion(_d2w);
921 NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
923 if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
924 !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y]) ) {
926 NR::Point const s_w( (*p) * _d2w );
928 gdouble x_to;
929 if ((*p)[NR::X] < dbox.min()[NR::X])
930 x_to = dbox.min()[NR::X];
931 else if ((*p)[NR::X] > dbox.max()[NR::X])
932 x_to = dbox.max()[NR::X];
933 else
934 x_to = (*p)[NR::X];
936 gdouble y_to;
937 if ((*p)[NR::Y] < dbox.min()[NR::Y])
938 y_to = dbox.min()[NR::Y];
939 else if ((*p)[NR::Y] > dbox.max()[NR::Y])
940 y_to = dbox.max()[NR::Y];
941 else
942 y_to = (*p)[NR::Y];
944 NR::Point const d_dt(x_to, y_to);
945 NR::Point const d_w( d_dt * _d2w );
946 NR::Point const moved_w( d_w - s_w );
948 if (autoscrollspeed == 0)
949 autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
951 if (autoscrollspeed != 0)
952 scroll_world (autoscrollspeed * moved_w);
954 return true;
955 }
956 return false;
957 }
959 void
960 SPDesktop::fullscreen()
961 {
962 _widget->setFullscreen();
963 }
965 void
966 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
967 {
968 _widget->getGeometry (x, y, w, h);
969 }
971 void
972 SPDesktop::setWindowPosition (NR::Point p)
973 {
974 _widget->setPosition (p);
975 }
977 void
978 SPDesktop::setWindowSize (gint w, gint h)
979 {
980 _widget->setSize (w, h);
981 }
983 void
984 SPDesktop::setWindowTransient (void *p, int transient_policy)
985 {
986 _widget->setTransient (p, transient_policy);
987 }
989 void
990 SPDesktop::presentWindow()
991 {
992 _widget->present();
993 }
995 bool
996 SPDesktop::warnDialog (gchar *text)
997 {
998 return _widget->warnDialog (text);
999 }
1001 void
1002 SPDesktop::toggleRulers()
1003 {
1004 _widget->toggleRulers();
1005 }
1007 void
1008 SPDesktop::toggleScrollbars()
1009 {
1010 _widget->toggleScrollbars();
1011 }
1013 void
1014 SPDesktop::layoutWidget()
1015 {
1016 _widget->layout();
1017 }
1019 void
1020 SPDesktop::destroyWidget()
1021 {
1022 _widget->destroy();
1023 }
1025 bool
1026 SPDesktop::shutdown()
1027 {
1028 return _widget->shutdown();
1029 }
1031 void
1032 SPDesktop::setToolboxFocusTo (gchar const *label)
1033 {
1034 _widget->setToolboxFocusTo (label);
1035 }
1037 void
1038 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1039 {
1040 _widget->setToolboxAdjustmentValue (id, val);
1041 }
1043 bool
1044 SPDesktop::isToolboxButtonActive (gchar const *id)
1045 {
1046 return _widget->isToolboxButtonActive (id);
1047 }
1049 void
1050 SPDesktop::emitToolSubselectionChanged(gpointer data)
1051 {
1052 _tool_subselection_changed.emit(data);
1053 inkscape_subselection_changed (this);
1054 }
1056 void
1057 SPDesktop::updateNow()
1058 {
1059 sp_canvas_update_now(canvas);
1060 }
1062 void
1063 SPDesktop::enableInteraction()
1064 {
1065 _widget->enableInteraction();
1066 }
1068 void SPDesktop::disableInteraction()
1069 {
1070 _widget->disableInteraction();
1071 }
1073 //----------------------------------------------------------------------
1074 // Callback implementations. The virtual ones are connected by the view.
1076 void
1077 SPDesktop::onPositionSet (double x, double y)
1078 {
1079 _widget->viewSetPosition (NR::Point(x,y));
1080 }
1082 void
1083 SPDesktop::onResized (double x, double y)
1084 {
1085 // Nothing called here
1086 }
1088 /**
1089 * Redraw callback; queues Gtk redraw; connected by View.
1090 */
1091 void
1092 SPDesktop::onRedrawRequested ()
1093 {
1094 if (main) {
1095 _widget->requestCanvasUpdate();
1096 }
1097 }
1099 void
1100 SPDesktop::updateCanvasNow()
1101 {
1102 _widget->requestCanvasUpdateAndWait();
1103 }
1105 /**
1106 * Associate document with desktop.
1107 */
1108 /// \todo fixme: refactor SPDesktop::init to use setDocument
1109 void
1110 SPDesktop::setDocument (SPDocument *doc)
1111 {
1112 if (this->doc() && doc) {
1113 namedview->hide(this);
1114 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1115 }
1117 if (_layer_hierarchy) {
1118 _layer_hierarchy->clear();
1119 delete _layer_hierarchy;
1120 }
1121 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1122 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1123 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1124 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1125 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1127 /* setup EventLog */
1128 event_log = new Inkscape::EventLog(doc);
1129 doc->addUndoObserver(*event_log);
1131 _commit_connection.disconnect();
1132 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1134 /// \todo fixme: This condition exists to make sure the code
1135 /// inside is called only once on initialization. But there
1136 /// are surely more safe methods to accomplish this.
1137 if (drawing) {
1138 NRArenaItem *ai;
1140 namedview = sp_document_namedview (doc, NULL);
1141 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1142 number = namedview->getViewCount();
1144 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1145 SP_CANVAS_ARENA (drawing)->arena,
1146 dkey,
1147 SP_ITEM_SHOW_DISPLAY);
1148 if (ai) {
1149 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1150 nr_arena_item_unref (ai);
1151 }
1152 namedview->show(this);
1153 /* Ugly hack */
1154 activate_guides (true);
1155 /* Ugly hack */
1156 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1157 }
1159 _document_replaced_signal.emit (this, doc);
1161 View::setDocument (doc);
1162 }
1164 void
1165 SPDesktop::onStatusMessage
1166 (Inkscape::MessageType type, gchar const *message)
1167 {
1168 if (_widget) {
1169 _widget->setMessage(type, message);
1170 }
1171 }
1173 void
1174 SPDesktop::onDocumentURISet (gchar const* uri)
1175 {
1176 _widget->setTitle(uri);
1177 }
1179 /**
1180 * Resized callback.
1181 */
1182 void
1183 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1184 {
1185 _doc2dt[5] = height;
1186 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1187 NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1188 SP_CTRLRECT(page)->setRectangle(a);
1189 SP_CTRLRECT(page_border)->setRectangle(a);
1190 }
1193 void
1194 SPDesktop::_onActivate (SPDesktop* dt)
1195 {
1196 if (!dt->_widget) return;
1197 dt->_widget->activateDesktop();
1198 }
1200 void
1201 SPDesktop::_onDeactivate (SPDesktop* dt)
1202 {
1203 if (!dt->_widget) return;
1204 dt->_widget->deactivateDesktop();
1205 }
1207 void
1208 SPDesktop::_onSelectionModified
1209 (Inkscape::Selection *selection, guint flags, SPDesktop *dt)
1210 {
1211 if (!dt->_widget) return;
1212 dt->_widget->updateScrollbars (expansion(dt->_d2w));
1213 }
1215 static void
1216 _onSelectionChanged
1217 (Inkscape::Selection *selection, SPDesktop *desktop)
1218 {
1219 /** \todo
1220 * only change the layer for single selections, or what?
1221 * This seems reasonable -- for multiple selections there can be many
1222 * different layers involved.
1223 */
1224 SPItem *item=selection->singleItem();
1225 if (item) {
1226 SPObject *layer=desktop->layerForObject(item);
1227 if ( layer && layer != desktop->currentLayer() ) {
1228 desktop->setCurrentLayer(layer);
1229 }
1230 }
1231 }
1233 /**
1234 * Calls event handler of current event context.
1235 * \param arena Unused
1236 * \todo fixme
1237 */
1238 static gint
1239 _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1240 {
1241 if (ai) {
1242 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1243 return sp_event_context_item_handler (desktop->event_context, spi, event);
1244 } else {
1245 return sp_event_context_root_handler (desktop->event_context, event);
1246 }
1247 }
1249 static void
1250 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1251 g_return_if_fail(SP_IS_GROUP(layer));
1252 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1253 }
1255 /// Callback
1256 static void
1257 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1258 g_return_if_fail(SP_IS_GROUP(layer));
1259 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1260 }
1262 /// Callback
1263 static void
1264 _layer_hierarchy_changed(SPObject *top, SPObject *bottom,
1265 SPDesktop *desktop)
1266 {
1267 desktop->_layer_changed_signal.emit (bottom);
1268 }
1270 /// Called when document is starting to be rebuilt.
1271 static void
1272 _reconstruction_start (SPDesktop * desktop)
1273 {
1274 // printf("Desktop, starting reconstruction\n");
1275 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1276 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1278 /*
1279 GSList const * selection_objs = desktop->selection->list();
1280 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1282 }
1283 */
1284 desktop->selection->clear();
1286 // printf("Desktop, starting reconstruction end\n");
1287 }
1289 /// Called when document rebuild is finished.
1290 static void
1291 _reconstruction_finish (SPDesktop * desktop)
1292 {
1293 // printf("Desktop, finishing reconstruction\n");
1294 if (desktop->_reconstruction_old_layer_id == NULL)
1295 return;
1297 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1298 if (newLayer != NULL)
1299 desktop->setCurrentLayer(newLayer);
1301 g_free(desktop->_reconstruction_old_layer_id);
1302 desktop->_reconstruction_old_layer_id = NULL;
1303 // printf("Desktop, finishing reconstruction end\n");
1304 return;
1305 }
1307 /**
1308 * Namedview_modified callback.
1309 */
1310 static void
1311 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1312 {
1313 SPNamedView *nv=SP_NAMEDVIEW(obj);
1315 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1317 /* Recalculate snap distances */
1318 /* FIXME: why is the desktop getting involved in setting up something
1319 ** that is entirely to do with the namedview?
1320 */
1321 _update_snap_distances (desktop);
1323 /* Show/hide page background */
1324 if (nv->pagecolor & 0xff) {
1325 sp_canvas_item_show (desktop->table);
1326 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1327 sp_canvas_item_move_to_z (desktop->table, 0);
1328 } else {
1329 sp_canvas_item_hide (desktop->table);
1330 }
1332 /* Show/hide page border */
1333 if (nv->showborder) {
1334 // show
1335 sp_canvas_item_show (desktop->page_border);
1336 // set color and shadow
1337 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1338 if (nv->pageshadow) {
1339 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1340 }
1341 // place in the z-order stack
1342 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1343 sp_canvas_item_move_to_z (desktop->page_border, 2);
1344 } else {
1345 int order = sp_canvas_item_order (desktop->page_border);
1346 int morder = sp_canvas_item_order (desktop->drawing);
1347 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1348 morder - order);
1349 }
1350 } else {
1351 sp_canvas_item_hide (desktop->page_border);
1352 if (nv->pageshadow) {
1353 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1354 }
1355 }
1357 /* Show/hide page shadow */
1358 if (nv->showpageshadow && nv->pageshadow) {
1359 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1360 } else {
1361 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1362 }
1364 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1365 (SP_RGBA32_R_U(nv->pagecolor) +
1366 SP_RGBA32_G_U(nv->pagecolor) +
1367 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1368 // the background color is light or transparent, use black outline
1369 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1370 } else { // use white outline
1371 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1372 }
1373 }
1374 }
1376 /**
1377 * Callback to reset snapper's distances.
1378 */
1379 static void
1380 _update_snap_distances (SPDesktop *desktop)
1381 {
1382 SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1384 SPNamedView &nv = *desktop->namedview;
1386 nv.snap_manager.grid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1387 *nv.gridtoleranceunit,
1388 px));
1389 nv.snap_manager.axonomgrid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1390 *nv.gridtoleranceunit,
1391 px));
1392 nv.snap_manager.guide.setDistance(sp_convert_distance_full(nv.guidetolerance,
1393 *nv.guidetoleranceunit,
1394 px));
1395 nv.snap_manager.object.setDistance(sp_convert_distance_full(nv.objecttolerance,
1396 *nv.objecttoleranceunit,
1397 px));
1398 }
1401 NR::Matrix SPDesktop::w2d() const
1402 {
1403 return _w2d;
1404 }
1406 NR::Point SPDesktop::w2d(NR::Point const &p) const
1407 {
1408 return p * _w2d;
1409 }
1411 NR::Point SPDesktop::d2w(NR::Point const &p) const
1412 {
1413 return p * _d2w;
1414 }
1416 NR::Matrix SPDesktop::doc2dt() const
1417 {
1418 return _doc2dt;
1419 }
1421 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1422 {
1423 return p * _doc2dt;
1424 }
1426 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1427 {
1428 return p / _doc2dt;
1429 }
1432 /**
1433 * Pop event context from desktop's context stack. Never used.
1434 */
1435 // void
1436 // SPDesktop::pop_event_context (unsigned int key)
1437 // {
1438 // SPEventContext *ec = NULL;
1439 //
1440 // if (event_context && event_context->key == key) {
1441 // g_return_if_fail (event_context);
1442 // g_return_if_fail (event_context->next);
1443 // ec = event_context;
1444 // sp_event_context_deactivate (ec);
1445 // event_context = ec->next;
1446 // sp_event_context_activate (event_context);
1447 // _event_context_changed_signal.emit (this, ec);
1448 // }
1449 //
1450 // SPEventContext *ref = event_context;
1451 // while (ref && ref->next && ref->next->key != key)
1452 // ref = ref->next;
1453 //
1454 // if (ref && ref->next) {
1455 // ec = ref->next;
1456 // ref->next = ec->next;
1457 // }
1458 //
1459 // if (ec) {
1460 // sp_event_context_finish (ec);
1461 // g_object_unref (G_OBJECT (ec));
1462 // }
1463 // }
1465 /*
1466 Local Variables:
1467 mode:c++
1468 c-file-style:"stroustrup"
1469 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1470 indent-tabs-mode:nil
1471 fill-column:99
1472 End:
1473 */
1474 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :