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 *
13 * Copyright (C) 2006 Johan Engelen <johan@shouraizou.nl>
14 * Copyright (C) 2006 John Bintz
15 * Copyright (C) 2004 MenTaLguY
16 * Copyright (C) 1999-2002 Lauris Kaplinski
17 * Copyright (C) 2000-2001 Ximian, Inc.
18 *
19 * Released under GNU GPL, read the file 'COPYING' for more information
20 */
22 /** \class SPDesktop
23 * SPDesktop is a subclass of View, implementing an editable document
24 * canvas. It is extensively used by many UI controls that need certain
25 * visual representations of their own.
26 *
27 * SPDesktop provides a certain set of SPCanvasItems, serving as GUI
28 * layers of different control objects. The one containing the whole
29 * document is the drawing layer. In addition to it, there are grid,
30 * guide, sketch and control layers. The sketch layer is used for
31 * temporary drawing objects, before the real objects in document are
32 * created. The control layer contains editing knots, rubberband and
33 * similar non-document UI objects.
34 *
35 * Each SPDesktop is associated with a SPNamedView node of the document
36 * tree. Currently, all desktops are created from a single main named
37 * view, but in the future there may be support for different ones.
38 * SPNamedView serves as an in-document container for desktop-related
39 * data, like grid and guideline placement, snapping options and so on.
40 *
41 * Associated with each SPDesktop are the two most important editing
42 * related objects - SPSelection and SPEventContext.
43 *
44 * Sodipodi keeps track of the active desktop and invokes notification
45 * signals whenever it changes. UI elements can use these to update their
46 * display to the selection of the currently active editing window.
47 * (Lauris Kaplinski)
48 */
50 #ifdef HAVE_CONFIG_H
51 # include "config.h"
52 #endif
54 #include <glibmm/i18n.h>
55 #include <sigc++/functors/mem_fun.h>
57 #include "macros.h"
58 #include "inkscape-private.h"
59 #include "desktop.h"
60 #include "desktop-events.h"
61 #include "desktop-handles.h"
62 #include "document.h"
63 #include "message-stack.h"
64 #include "selection.h"
65 #include "select-context.h"
66 #include "sp-namedview.h"
67 #include "color.h"
68 #include "sp-item-group.h"
69 #include "prefs-utils.h"
70 #include "object-hierarchy.h"
71 #include "helper/units.h"
72 #include "display/canvas-arena.h"
73 #include "display/nr-arena.h"
74 #include "display/gnome-canvas-acetate.h"
75 #include "display/sodipodi-ctrlrect.h"
76 #include "display/sp-canvas-util.h"
77 #include "libnr/nr-matrix-div.h"
78 #include "libnr/nr-rect-ops.h"
79 #include "ui/dialog/dialog-manager.h"
80 #include "xml/repr.h"
81 #include "message-context.h"
82 #include "layer-manager.h"
83 #include "event-log.h"
85 namespace Inkscape { namespace XML { class Node; }}
87 // Callback declarations
88 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
89 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
90 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
91 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
92 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
93 static void _reconstruction_start(SPDesktop * desktop);
94 static void _reconstruction_finish(SPDesktop * desktop);
95 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
96 static void _update_snap_distances (SPDesktop *desktop);
98 /**
99 * Return new desktop object.
100 * \pre namedview != NULL.
101 * \pre canvas != NULL.
102 */
103 SPDesktop::SPDesktop()
104 {
105 _dlg_mgr = NULL;
106 _widget = 0;
107 namedview = NULL;
108 selection = NULL;
109 acetate = NULL;
110 main = NULL;
111 grid = NULL;
112 guides = NULL;
113 drawing = NULL;
114 sketch = NULL;
115 controls = NULL;
116 event_context = 0;
117 layer_manager = 0;
119 _d2w.set_identity();
120 _w2d.set_identity();
121 _doc2dt = NR::Matrix(NR::scale(1, -1));
123 guides_active = false;
125 zooms_past = NULL;
126 zooms_future = NULL;
128 is_fullscreen = false;
130 gr_item = NULL;
131 gr_point_type = 0;
132 gr_point_i = 0;
133 gr_fill_or_stroke = true;
135 _layer_hierarchy = NULL;
136 _active = false;
138 selection = Inkscape::GC::release (new Inkscape::Selection (this));
139 }
141 void
142 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
144 {
145 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
147 current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
149 namedview = nv;
150 canvas = aCanvas;
152 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
153 /* Kill flicker */
154 sp_document_ensure_up_to_date (document);
156 /* Setup Dialog Manager */
157 _dlg_mgr = new Inkscape::UI::Dialog::DialogManager();
159 dkey = sp_item_display_key_new (1);
161 /* Connect document */
162 setDocument (document);
164 number = namedview->getViewCount();
167 /* Setup Canvas */
168 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
170 SPCanvasGroup *root = sp_canvas_root (canvas);
172 /* Setup adminstrative layers */
173 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
174 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
175 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
176 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
178 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
179 SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
180 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
181 sp_canvas_item_move_to_z (table, 0);
183 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
184 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
185 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
187 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
188 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
190 SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
192 if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
193 // Start in outline mode
194 setDisplayModeOutline();
195 } else {
196 // Start in normal mode, default
197 setDisplayModeNormal();
198 }
200 grid = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
201 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
202 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
203 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
205 /* Push select tool to the bottom of stack */
206 /** \todo
207 * FIXME: this is the only call to this. Everything else seems to just
208 * call "set" instead of "push". Can we assume that there is only one
209 * context ever?
210 */
211 push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
213 // display rect and zoom are now handled in sp_desktop_widget_realize()
215 NR::Rect const d(NR::Point(0.0, 0.0),
216 NR::Point(sp_document_width(document), sp_document_height(document)));
218 SP_CTRLRECT(page)->setRectangle(d);
219 SP_CTRLRECT(page_border)->setRectangle(d);
221 /* the following sets the page shadow on the canvas
222 It was originally set to 5, which is really cheesy!
223 It now is an attribute in the document's namedview. If a value of
224 0 is used, then the constructor for a shadow is not initialized.
225 */
227 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
228 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
229 }
232 /* Connect event for page resize */
233 _doc2dt[5] = sp_document_height (document);
234 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
236 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
238 NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
239 SP_CANVAS_ARENA (drawing)->arena,
240 dkey,
241 SP_ITEM_SHOW_DISPLAY);
242 if (ai) {
243 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
244 nr_arena_item_unref (ai);
245 }
247 namedview->show(this);
248 /* Ugly hack */
249 activate_guides (true);
250 /* Ugly hack */
251 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
253 /* Set up notification of rebuilding the document, this allows
254 for saving object related settings in the document. */
255 _reconstruction_start_connection =
256 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
257 _reconstruction_finish_connection =
258 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
259 _reconstruction_old_layer_id = NULL;
261 _commit_connection = document->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
263 // ?
264 // sp_active_desktop_set (desktop);
265 _inkscape = INKSCAPE;
267 _activate_connection = _activate_signal.connect(
268 sigc::bind(
269 sigc::ptr_fun(_onActivate),
270 this
271 )
272 );
273 _deactivate_connection = _deactivate_signal.connect(
274 sigc::bind(
275 sigc::ptr_fun(_onDeactivate),
276 this
277 )
278 );
280 _sel_modified_connection = selection->connectModified(
281 sigc::bind(
282 sigc::ptr_fun(&_onSelectionModified),
283 this
284 )
285 );
286 _sel_changed_connection = selection->connectChanged(
287 sigc::bind(
288 sigc::ptr_fun(&_onSelectionChanged),
289 this
290 )
291 );
294 /* setup LayerManager */
295 // (Setting up after the connections are all in place, as it may use some of them)
296 layer_manager = new Inkscape::LayerManager( this );
297 }
300 void SPDesktop::destroy()
301 {
302 _activate_connection.disconnect();
303 _deactivate_connection.disconnect();
304 _sel_modified_connection.disconnect();
305 _sel_changed_connection.disconnect();
306 _modified_connection.disconnect();
307 _commit_connection.disconnect();
308 _reconstruction_start_connection.disconnect();
309 _reconstruction_finish_connection.disconnect();
311 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
312 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
313 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
315 while (event_context) {
316 SPEventContext *ec = event_context;
317 event_context = ec->next;
318 sp_event_context_finish (ec);
319 g_object_unref (G_OBJECT (ec));
320 }
322 if (_layer_hierarchy) {
323 delete _layer_hierarchy;
324 }
326 if (_inkscape) {
327 _inkscape = NULL;
328 }
330 if (drawing) {
331 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
332 drawing = NULL;
333 }
335 delete _guides_message_context;
336 _guides_message_context = NULL;
338 g_list_free (zooms_past);
339 g_list_free (zooms_future);
340 }
342 SPDesktop::~SPDesktop() {}
344 //--------------------------------------------------------------------
345 /* Public methods */
347 void SPDesktop::setDisplayModeNormal()
348 {
349 prefs_set_int_attribute("options.outlinemode", "value", 0);
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 prefs_set_int_attribute("options.outlinemode", "value", 1);
360 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
361 canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
362 displayMode = RENDERMODE_OUTLINE;
363 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
364 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
365 }
367 void SPDesktop::displayModeToggle()
368 {
369 if (prefs_get_int_attribute("options.outlinemode", "value", prefs_get_int_attribute("options.startmode", "outline", 0)))
370 setDisplayModeNormal();
371 else
372 setDisplayModeOutline();
373 }
375 /**
376 * Returns current root (=bottom) layer.
377 */
378 SPObject *SPDesktop::currentRoot() const
379 {
380 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
381 }
383 /**
384 * Returns current top layer.
385 */
386 SPObject *SPDesktop::currentLayer() const
387 {
388 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
389 }
391 /**
392 * Sets the current layer of the desktop.
393 *
394 * Make \a object the top layer.
395 */
396 void SPDesktop::setCurrentLayer(SPObject *object) {
397 g_return_if_fail(SP_IS_GROUP(object));
398 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
399 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
400 _layer_hierarchy->setBottom(object);
401 }
403 /**
404 * Return layer that contains \a object.
405 */
406 SPObject *SPDesktop::layerForObject(SPObject *object) {
407 g_return_val_if_fail(object != NULL, NULL);
409 SPObject *root=currentRoot();
410 object = SP_OBJECT_PARENT(object);
411 while ( object && object != root && !isLayer(object) ) {
412 object = SP_OBJECT_PARENT(object);
413 }
414 return object;
415 }
417 /**
418 * True if object is a layer.
419 */
420 bool SPDesktop::isLayer(SPObject *object) const {
421 return ( SP_IS_GROUP(object)
422 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
423 == SPGroup::LAYER ) );
424 }
426 /**
427 * True if desktop viewport fully contains \a item's bbox.
428 */
429 bool SPDesktop::isWithinViewport (SPItem *item) const
430 {
431 NR::Rect const viewport = get_display_area();
432 NR::Rect const bbox = sp_item_bbox_desktop(item);
433 return viewport.contains(bbox);
434 }
436 ///
437 bool SPDesktop::itemIsHidden(SPItem const *item) const {
438 return item->isHidden(this->dkey);
439 }
441 /**
442 * Set activate property of desktop; emit signal if changed.
443 */
444 void
445 SPDesktop::set_active (bool new_active)
446 {
447 if (new_active != _active) {
448 _active = new_active;
449 if (new_active) {
450 _activate_signal.emit();
451 } else {
452 _deactivate_signal.emit();
453 }
454 }
455 }
457 /**
458 * Set activate status of current desktop's named view.
459 */
460 void
461 SPDesktop::activate_guides(bool activate)
462 {
463 guides_active = activate;
464 namedview->activateGuides(this, activate);
465 }
467 /**
468 * Make desktop switch documents.
469 */
470 void
471 SPDesktop::change_document (SPDocument *theDocument)
472 {
473 g_return_if_fail (theDocument != NULL);
475 /* unselect everything before switching documents */
476 selection->clear();
478 setDocument (theDocument);
479 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
480 _document_replaced_signal.emit (this, theDocument);
481 }
483 /**
484 * Make desktop switch event contexts.
485 */
486 void
487 SPDesktop::set_event_context (GtkType type, const gchar *config)
488 {
489 SPEventContext *ec;
490 while (event_context) {
491 ec = event_context;
492 sp_event_context_deactivate (ec);
493 event_context = ec->next;
494 sp_event_context_finish (ec);
495 g_object_unref (G_OBJECT (ec));
496 }
498 Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
499 ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
500 ec->next = event_context;
501 event_context = ec;
502 sp_event_context_activate (ec);
503 _event_context_changed_signal.emit (this, ec);
504 }
506 /**
507 * Push event context onto desktop's context stack.
508 */
509 void
510 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
511 {
512 SPEventContext *ref, *ec;
513 Inkscape::XML::Node *repr;
515 if (event_context && event_context->key == key) return;
516 ref = event_context;
517 while (ref && ref->next && ref->next->key != key) ref = ref->next;
518 if (ref && ref->next) {
519 ec = ref->next;
520 ref->next = ec->next;
521 sp_event_context_finish (ec);
522 g_object_unref (G_OBJECT (ec));
523 }
525 if (event_context) sp_event_context_deactivate (event_context);
526 repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
527 ec = sp_event_context_new (type, this, repr, key);
528 ec->next = event_context;
529 event_context = ec;
530 sp_event_context_activate (ec);
531 _event_context_changed_signal.emit (this, ec);
532 }
534 /**
535 * Sets the coordinate status to a given point
536 */
537 void
538 SPDesktop::set_coordinate_status (NR::Point p) {
539 _widget->setCoordinateStatus(p);
540 }
542 /**
543 * \see sp_document_item_from_list_at_point_bottom()
544 */
545 SPItem *
546 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
547 {
548 g_return_val_if_fail (doc() != NULL, NULL);
549 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
550 }
552 /**
553 * \see sp_document_item_at_point()
554 */
555 SPItem *
556 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
557 {
558 g_return_val_if_fail (doc() != NULL, NULL);
559 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
560 }
562 /**
563 * \see sp_document_group_at_point()
564 */
565 SPItem *
566 SPDesktop::group_at_point (NR::Point const p) const
567 {
568 g_return_val_if_fail (doc() != NULL, NULL);
569 return sp_document_group_at_point (doc(), dkey, p);
570 }
572 /**
573 * \brief Returns the mouse point in document coordinates; if mouse is
574 * outside the canvas, returns the center of canvas viewpoint
575 */
576 NR::Point
577 SPDesktop::point() const
578 {
579 NR::Point p = _widget->getPointer();
580 NR::Point pw = sp_canvas_window_to_world (canvas, p);
581 p = w2d(pw);
583 NR::Rect const r = canvas->getViewbox();
585 NR::Point r0 = w2d(r.min());
586 NR::Point r1 = w2d(r.max());
588 if (p[NR::X] >= r0[NR::X] &&
589 p[NR::X] <= r1[NR::X] &&
590 p[NR::Y] >= r1[NR::Y] &&
591 p[NR::Y] <= r0[NR::Y])
592 {
593 return p;
594 } else {
595 return (r0 + r1) / 2;
596 }
597 }
599 /**
600 * Put current zoom data in history list.
601 */
602 void
603 SPDesktop::push_current_zoom (GList **history)
604 {
605 NR::Rect const area = get_display_area();
607 NRRect *old_zoom = g_new(NRRect, 1);
608 old_zoom->x0 = area.min()[NR::X];
609 old_zoom->x1 = area.max()[NR::X];
610 old_zoom->y0 = area.min()[NR::Y];
611 old_zoom->y1 = area.max()[NR::Y];
612 if ( *history == NULL
613 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
614 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
615 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
616 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
617 {
618 *history = g_list_prepend (*history, old_zoom);
619 }
620 }
622 /**
623 * Set viewbox.
624 */
625 void
626 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
627 {
628 g_assert(_widget);
630 // save the zoom
631 if (log) {
632 push_current_zoom(&zooms_past);
633 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
634 g_list_free (zooms_future);
635 zooms_future = NULL;
636 }
638 double const cx = 0.5 * (x0 + x1);
639 double const cy = 0.5 * (y0 + y1);
641 NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
643 double scale = expansion(_d2w);
644 double newscale;
645 if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
646 newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
647 } else {
648 newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
649 }
651 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
653 int clear = FALSE;
654 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
655 /* Set zoom factors */
656 _d2w = NR::Matrix(NR::scale(newscale, -newscale));
657 _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
658 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
659 clear = TRUE;
660 }
662 /* Calculate top left corner */
663 x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
664 y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
666 /* Scroll */
667 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
669 _widget->updateRulers();
670 _widget->updateScrollbars(expansion(_d2w));
671 _widget->updateZoom();
672 }
674 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
675 {
676 set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
677 }
679 /**
680 * Return viewbox dimensions.
681 */
682 NR::Rect SPDesktop::get_display_area() const
683 {
684 NR::Rect const viewbox = canvas->getViewbox();
686 double const scale = _d2w[0];
688 return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
689 NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
690 }
692 /**
693 * Revert back to previous zoom if possible.
694 */
695 void
696 SPDesktop::prev_zoom()
697 {
698 if (zooms_past == NULL) {
699 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
700 return;
701 }
703 // push current zoom into forward zooms list
704 push_current_zoom (&zooms_future);
706 // restore previous zoom
707 set_display_area (((NRRect *) zooms_past->data)->x0,
708 ((NRRect *) zooms_past->data)->y0,
709 ((NRRect *) zooms_past->data)->x1,
710 ((NRRect *) zooms_past->data)->y1,
711 0, false);
713 // remove the just-added zoom from the past zooms list
714 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
715 }
717 /**
718 * Set zoom to next in list.
719 */
720 void
721 SPDesktop::next_zoom()
722 {
723 if (zooms_future == NULL) {
724 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
725 return;
726 }
728 // push current zoom into past zooms list
729 push_current_zoom (&zooms_past);
731 // restore next zoom
732 set_display_area (((NRRect *) zooms_future->data)->x0,
733 ((NRRect *) zooms_future->data)->y0,
734 ((NRRect *) zooms_future->data)->x1,
735 ((NRRect *) zooms_future->data)->y1,
736 0, false);
738 // remove the just-used zoom from the zooms_future list
739 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
740 }
742 /**
743 * Zoom to point with absolute zoom factor.
744 */
745 void
746 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
747 {
748 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
750 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
751 // this check prevents "sliding" when trying to zoom in at maximum zoom;
752 /// \todo someone please fix calculations properly and remove this hack
753 if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
754 return;
756 NR::Rect const viewbox = canvas->getViewbox();
758 double const width2 = viewbox.dimensions()[NR::X] / zoom;
759 double const height2 = viewbox.dimensions()[NR::Y] / zoom;
761 set_display_area(cx - px * width2,
762 cy - py * height2,
763 cx + (1 - px) * width2,
764 cy + (1 - py) * height2,
765 0.0);
766 }
768 /**
769 * Zoom to center with absolute zoom factor.
770 */
771 void
772 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
773 {
774 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
775 }
777 /**
778 * Zoom to point with relative zoom factor.
779 */
780 void
781 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
782 {
783 NR::Rect const area = get_display_area();
785 if (cx < area.min()[NR::X]) {
786 cx = area.min()[NR::X];
787 }
788 if (cx > area.max()[NR::X]) {
789 cx = area.max()[NR::X];
790 }
791 if (cy < area.min()[NR::Y]) {
792 cy = area.min()[NR::Y];
793 }
794 if (cy > area.max()[NR::Y]) {
795 cy = area.max()[NR::Y];
796 }
798 gdouble const scale = expansion(_d2w) * zoom;
799 double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
800 double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
802 zoom_absolute_keep_point(cx, cy, px, py, scale);
803 }
805 /**
806 * Zoom to center with relative zoom factor.
807 */
808 void
809 SPDesktop::zoom_relative (double cx, double cy, double zoom)
810 {
811 gdouble scale = expansion(_d2w) * zoom;
812 zoom_absolute (cx, cy, scale);
813 }
815 /**
816 * Set display area to origin and current document dimensions.
817 */
818 void
819 SPDesktop::zoom_page()
820 {
821 NR::Rect d(NR::Point(0, 0),
822 NR::Point(sp_document_width(doc()), sp_document_height(doc())));
824 if (d.dimensions()[NR::X] < 1.0 || d.dimensions()[NR::Y] < 1.0) {
825 return;
826 }
828 set_display_area(d, 10);
829 }
831 /**
832 * Set display area to current document width.
833 */
834 void
835 SPDesktop::zoom_page_width()
836 {
837 NR::Rect const a = get_display_area();
839 if (sp_document_width(doc()) < 1.0) {
840 return;
841 }
843 NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
844 NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
846 set_display_area(d, 10);
847 }
849 /**
850 * Zoom to selection.
851 */
852 void
853 SPDesktop::zoom_selection()
854 {
855 NR::Rect const d = selection->bounds();
857 if (d.dimensions()[NR::X] < 0.1 || d.dimensions()[NR::Y] < 0.1) {
858 return;
859 }
861 set_display_area(d, 10);
862 }
864 /**
865 * Tell widget to let zoom widget grab keyboard focus.
866 */
867 void
868 SPDesktop::zoom_grab_focus()
869 {
870 _widget->letZoomGrabFocus();
871 }
873 /**
874 * Zoom to whole drawing.
875 */
876 void
877 SPDesktop::zoom_drawing()
878 {
879 g_return_if_fail (doc() != NULL);
880 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
881 g_return_if_fail (docitem != NULL);
883 NR::Rect d = sp_item_bbox_desktop(docitem);
885 /* Note that the second condition here indicates that
886 ** there are no items in the drawing.
887 */
888 if ( d.dimensions()[NR::X] < 1.0 || d.dimensions()[NR::Y] < 1.0 ) {
889 return;
890 }
892 set_display_area(d, 10);
893 }
895 /**
896 * Scroll canvas by specific coordinate amount.
897 */
898 void
899 SPDesktop::scroll_world (double dx, double dy)
900 {
901 g_assert(_widget);
903 NR::Rect const viewbox = canvas->getViewbox();
905 sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE);
907 _widget->updateRulers();
908 _widget->updateScrollbars(expansion(_d2w));
909 }
911 bool
912 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
913 {
914 gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
916 // autoscrolldistance is in screen pixels, but the display area is in document units
917 autoscrolldistance /= expansion(_d2w);
918 NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
920 if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
921 !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y]) ) {
923 NR::Point const s_w( (*p) * _d2w );
925 gdouble x_to;
926 if ((*p)[NR::X] < dbox.min()[NR::X])
927 x_to = dbox.min()[NR::X];
928 else if ((*p)[NR::X] > dbox.max()[NR::X])
929 x_to = dbox.max()[NR::X];
930 else
931 x_to = (*p)[NR::X];
933 gdouble y_to;
934 if ((*p)[NR::Y] < dbox.min()[NR::Y])
935 y_to = dbox.min()[NR::Y];
936 else if ((*p)[NR::Y] > dbox.max()[NR::Y])
937 y_to = dbox.max()[NR::Y];
938 else
939 y_to = (*p)[NR::Y];
941 NR::Point const d_dt(x_to, y_to);
942 NR::Point const d_w( d_dt * _d2w );
943 NR::Point const moved_w( d_w - s_w );
945 if (autoscrollspeed == 0)
946 autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
948 if (autoscrollspeed != 0)
949 scroll_world (autoscrollspeed * moved_w);
951 return true;
952 }
953 return false;
954 }
956 void
957 SPDesktop::fullscreen()
958 {
959 _widget->setFullscreen();
960 }
962 void
963 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
964 {
965 _widget->getGeometry (x, y, w, h);
966 }
968 void
969 SPDesktop::setWindowPosition (NR::Point p)
970 {
971 _widget->setPosition (p);
972 }
974 void
975 SPDesktop::setWindowSize (gint w, gint h)
976 {
977 _widget->setSize (w, h);
978 }
980 void
981 SPDesktop::setWindowTransient (void *p, int transient_policy)
982 {
983 _widget->setTransient (p, transient_policy);
984 }
986 void
987 SPDesktop::presentWindow()
988 {
989 _widget->present();
990 }
992 bool
993 SPDesktop::warnDialog (gchar *text)
994 {
995 return _widget->warnDialog (text);
996 }
998 void
999 SPDesktop::toggleRulers()
1000 {
1001 _widget->toggleRulers();
1002 }
1004 void
1005 SPDesktop::toggleScrollbars()
1006 {
1007 _widget->toggleScrollbars();
1008 }
1010 void
1011 SPDesktop::layoutWidget()
1012 {
1013 _widget->layout();
1014 }
1016 void
1017 SPDesktop::destroyWidget()
1018 {
1019 _widget->destroy();
1020 }
1022 bool
1023 SPDesktop::shutdown()
1024 {
1025 return _widget->shutdown();
1026 }
1028 void
1029 SPDesktop::setToolboxFocusTo (gchar const *label)
1030 {
1031 _widget->setToolboxFocusTo (label);
1032 }
1034 void
1035 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1036 {
1037 _widget->setToolboxAdjustmentValue (id, val);
1038 }
1040 bool
1041 SPDesktop::isToolboxButtonActive (gchar const *id)
1042 {
1043 return _widget->isToolboxButtonActive (id);
1044 }
1046 void
1047 SPDesktop::emitToolSubselectionChanged(gpointer data)
1048 {
1049 _tool_subselection_changed.emit(data);
1050 inkscape_subselection_changed (this);
1051 }
1053 void
1054 SPDesktop::updateNow()
1055 {
1056 sp_canvas_update_now(canvas);
1057 }
1059 void
1060 SPDesktop::enableInteraction()
1061 {
1062 _widget->enableInteraction();
1063 }
1065 void SPDesktop::disableInteraction()
1066 {
1067 _widget->disableInteraction();
1068 }
1070 //----------------------------------------------------------------------
1071 // Callback implementations. The virtual ones are connected by the view.
1073 void
1074 SPDesktop::onPositionSet (double x, double y)
1075 {
1076 _widget->viewSetPosition (NR::Point(x,y));
1077 }
1079 void
1080 SPDesktop::onResized (double x, double y)
1081 {
1082 // Nothing called here
1083 }
1085 /**
1086 * Redraw callback; queues Gtk redraw; connected by View.
1087 */
1088 void
1089 SPDesktop::onRedrawRequested ()
1090 {
1091 if (main) {
1092 _widget->requestCanvasUpdate();
1093 }
1094 }
1096 void
1097 SPDesktop::updateCanvasNow()
1098 {
1099 _widget->requestCanvasUpdateAndWait();
1100 }
1102 /**
1103 * Associate document with desktop.
1104 */
1105 /// \todo fixme: refactor SPDesktop::init to use setDocument
1106 void
1107 SPDesktop::setDocument (SPDocument *doc)
1108 {
1109 if (this->doc() && doc) {
1110 namedview->hide(this);
1111 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1112 }
1114 if (_layer_hierarchy) {
1115 _layer_hierarchy->clear();
1116 delete _layer_hierarchy;
1117 }
1118 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1119 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1120 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1121 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1122 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1124 /* setup EventLog */
1125 event_log = new Inkscape::EventLog(doc);
1126 doc->addUndoObserver(*event_log);
1128 _commit_connection.disconnect();
1129 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1131 /// \todo fixme: This condition exists to make sure the code
1132 /// inside is called only once on initialization. But there
1133 /// are surely more safe methods to accomplish this.
1134 if (drawing) {
1135 NRArenaItem *ai;
1137 namedview = sp_document_namedview (doc, NULL);
1138 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1139 number = namedview->getViewCount();
1141 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1142 SP_CANVAS_ARENA (drawing)->arena,
1143 dkey,
1144 SP_ITEM_SHOW_DISPLAY);
1145 if (ai) {
1146 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1147 nr_arena_item_unref (ai);
1148 }
1149 namedview->show(this);
1150 /* Ugly hack */
1151 activate_guides (true);
1152 /* Ugly hack */
1153 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1154 }
1156 _document_replaced_signal.emit (this, doc);
1158 View::setDocument (doc);
1159 }
1161 void
1162 SPDesktop::onStatusMessage
1163 (Inkscape::MessageType type, gchar const *message)
1164 {
1165 if (_widget) {
1166 _widget->setMessage(type, message);
1167 }
1168 }
1170 void
1171 SPDesktop::onDocumentURISet (gchar const* uri)
1172 {
1173 _widget->setTitle(uri);
1174 }
1176 /**
1177 * Resized callback.
1178 */
1179 void
1180 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1181 {
1182 _doc2dt[5] = height;
1183 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1184 NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1185 SP_CTRLRECT(page)->setRectangle(a);
1186 SP_CTRLRECT(page_border)->setRectangle(a);
1187 }
1190 void
1191 SPDesktop::_onActivate (SPDesktop* dt)
1192 {
1193 if (!dt->_widget) return;
1194 dt->_widget->activateDesktop();
1195 }
1197 void
1198 SPDesktop::_onDeactivate (SPDesktop* dt)
1199 {
1200 if (!dt->_widget) return;
1201 dt->_widget->deactivateDesktop();
1202 }
1204 void
1205 SPDesktop::_onSelectionModified
1206 (Inkscape::Selection *selection, guint flags, SPDesktop *dt)
1207 {
1208 if (!dt->_widget) return;
1209 dt->_widget->updateScrollbars (expansion(dt->_d2w));
1210 }
1212 static void
1213 _onSelectionChanged
1214 (Inkscape::Selection *selection, SPDesktop *desktop)
1215 {
1216 /** \todo
1217 * only change the layer for single selections, or what?
1218 * This seems reasonable -- for multiple selections there can be many
1219 * different layers involved.
1220 */
1221 SPItem *item=selection->singleItem();
1222 if (item) {
1223 SPObject *layer=desktop->layerForObject(item);
1224 if ( layer && layer != desktop->currentLayer() ) {
1225 desktop->setCurrentLayer(layer);
1226 }
1227 }
1228 }
1230 /**
1231 * Calls event handler of current event context.
1232 * \param arena Unused
1233 * \todo fixme
1234 */
1235 static gint
1236 _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1237 {
1238 if (ai) {
1239 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1240 return sp_event_context_item_handler (desktop->event_context, spi, event);
1241 } else {
1242 return sp_event_context_root_handler (desktop->event_context, event);
1243 }
1244 }
1246 static void
1247 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1248 g_return_if_fail(SP_IS_GROUP(layer));
1249 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1250 }
1252 /// Callback
1253 static void
1254 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1255 g_return_if_fail(SP_IS_GROUP(layer));
1256 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1257 }
1259 /// Callback
1260 static void
1261 _layer_hierarchy_changed(SPObject *top, SPObject *bottom,
1262 SPDesktop *desktop)
1263 {
1264 desktop->_layer_changed_signal.emit (bottom);
1265 }
1267 /// Called when document is starting to be rebuilt.
1268 static void
1269 _reconstruction_start (SPDesktop * desktop)
1270 {
1271 // printf("Desktop, starting reconstruction\n");
1272 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1273 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1275 /*
1276 GSList const * selection_objs = desktop->selection->list();
1277 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1279 }
1280 */
1281 desktop->selection->clear();
1283 // printf("Desktop, starting reconstruction end\n");
1284 }
1286 /// Called when document rebuild is finished.
1287 static void
1288 _reconstruction_finish (SPDesktop * desktop)
1289 {
1290 // printf("Desktop, finishing reconstruction\n");
1291 if (desktop->_reconstruction_old_layer_id == NULL)
1292 return;
1294 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1295 if (newLayer != NULL)
1296 desktop->setCurrentLayer(newLayer);
1298 g_free(desktop->_reconstruction_old_layer_id);
1299 desktop->_reconstruction_old_layer_id = NULL;
1300 // printf("Desktop, finishing reconstruction end\n");
1301 return;
1302 }
1304 /**
1305 * Namedview_modified callback.
1306 */
1307 static void
1308 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1309 {
1310 SPNamedView *nv=SP_NAMEDVIEW(obj);
1312 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1314 /* Recalculate snap distances */
1315 /* FIXME: why is the desktop getting involved in setting up something
1316 ** that is entirely to do with the namedview?
1317 */
1318 _update_snap_distances (desktop);
1320 /* Show/hide page background */
1321 if (nv->pagecolor & 0xff) {
1322 sp_canvas_item_show (desktop->table);
1323 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1324 sp_canvas_item_move_to_z (desktop->table, 0);
1325 } else {
1326 sp_canvas_item_hide (desktop->table);
1327 }
1329 /* Show/hide page border */
1330 if (nv->showborder) {
1331 // show
1332 sp_canvas_item_show (desktop->page_border);
1333 // set color and shadow
1334 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1335 if (nv->pageshadow) {
1336 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1337 }
1338 // place in the z-order stack
1339 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1340 sp_canvas_item_move_to_z (desktop->page_border, 2);
1341 } else {
1342 int order = sp_canvas_item_order (desktop->page_border);
1343 int morder = sp_canvas_item_order (desktop->drawing);
1344 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1345 morder - order);
1346 }
1347 } else {
1348 sp_canvas_item_hide (desktop->page_border);
1349 if (nv->pageshadow) {
1350 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1351 }
1352 }
1354 /* Show/hide page shadow */
1355 if (nv->showpageshadow && nv->pageshadow) {
1356 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1357 } else {
1358 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1359 }
1361 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1362 (SP_RGBA32_R_U(nv->pagecolor) +
1363 SP_RGBA32_G_U(nv->pagecolor) +
1364 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1365 // the background color is light or transparent, use black outline
1366 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1367 } else { // use white outline
1368 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1369 }
1370 }
1371 }
1373 /**
1374 * Callback to reset snapper's distances.
1375 */
1376 static void
1377 _update_snap_distances (SPDesktop *desktop)
1378 {
1379 SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1381 SPNamedView &nv = *desktop->namedview;
1383 nv.snap_manager.grid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1384 *nv.gridtoleranceunit,
1385 px));
1386 nv.snap_manager.axonomgrid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1387 *nv.gridtoleranceunit,
1388 px));
1389 nv.snap_manager.guide.setDistance(sp_convert_distance_full(nv.guidetolerance,
1390 *nv.guidetoleranceunit,
1391 px));
1392 nv.snap_manager.object.setDistance(sp_convert_distance_full(nv.objecttolerance,
1393 *nv.objecttoleranceunit,
1394 px));
1395 }
1398 NR::Matrix SPDesktop::w2d() const
1399 {
1400 return _w2d;
1401 }
1403 NR::Point SPDesktop::w2d(NR::Point const &p) const
1404 {
1405 return p * _w2d;
1406 }
1408 NR::Point SPDesktop::d2w(NR::Point const &p) const
1409 {
1410 return p * _d2w;
1411 }
1413 NR::Matrix SPDesktop::doc2dt() const
1414 {
1415 return _doc2dt;
1416 }
1418 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1419 {
1420 return p * _doc2dt;
1421 }
1423 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1424 {
1425 return p / _doc2dt;
1426 }
1429 /**
1430 * Pop event context from desktop's context stack. Never used.
1431 */
1432 // void
1433 // SPDesktop::pop_event_context (unsigned int key)
1434 // {
1435 // SPEventContext *ec = NULL;
1436 //
1437 // if (event_context && event_context->key == key) {
1438 // g_return_if_fail (event_context);
1439 // g_return_if_fail (event_context->next);
1440 // ec = event_context;
1441 // sp_event_context_deactivate (ec);
1442 // event_context = ec->next;
1443 // sp_event_context_activate (event_context);
1444 // _event_context_changed_signal.emit (this, ec);
1445 // }
1446 //
1447 // SPEventContext *ref = event_context;
1448 // while (ref && ref->next && ref->next->key != key)
1449 // ref = ref->next;
1450 //
1451 // if (ref && ref->next) {
1452 // ec = ref->next;
1453 // ref->next = ec->next;
1454 // }
1455 //
1456 // if (ec) {
1457 // sp_event_context_finish (ec);
1458 // g_object_unref (G_OBJECT (ec));
1459 // }
1460 // }
1462 /*
1463 Local Variables:
1464 mode:c++
1465 c-file-style:"stroustrup"
1466 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1467 indent-tabs-mode:nil
1468 fill-column:99
1469 End:
1470 */
1471 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :