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_num = 0;
132 gr_fill_or_stroke = true;
134 _layer_hierarchy = NULL;
135 _active = false;
137 selection = Inkscape::GC::release (new Inkscape::Selection (this));
138 }
140 void
141 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
143 {
144 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
146 current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
148 namedview = nv;
149 canvas = aCanvas;
151 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
152 /* Kill flicker */
153 sp_document_ensure_up_to_date (document);
155 /* Setup Dialog Manager */
156 _dlg_mgr = new Inkscape::UI::Dialog::DialogManager();
158 dkey = sp_item_display_key_new (1);
160 /* Connect document */
161 setDocument (document);
163 number = namedview->getViewCount();
166 /* Setup Canvas */
167 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
169 SPCanvasGroup *root = sp_canvas_root (canvas);
171 /* Setup adminstrative layers */
172 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
173 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
174 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
175 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
177 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
178 SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
179 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
180 sp_canvas_item_move_to_z (table, 0);
182 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
183 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
184 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
186 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
187 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
189 SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
191 if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
192 // Start in outline mode
193 setDisplayModeOutline();
194 } else {
195 // Start in normal mode, default
196 setDisplayModeNormal();
197 }
199 grid = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
200 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
201 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
202 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
204 /* Push select tool to the bottom of stack */
205 /** \todo
206 * FIXME: this is the only call to this. Everything else seems to just
207 * call "set" instead of "push". Can we assume that there is only one
208 * context ever?
209 */
210 push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
212 // display rect and zoom are now handled in sp_desktop_widget_realize()
214 NR::Rect const d(NR::Point(0.0, 0.0),
215 NR::Point(sp_document_width(document), sp_document_height(document)));
217 SP_CTRLRECT(page)->setRectangle(d);
218 SP_CTRLRECT(page_border)->setRectangle(d);
220 /* the following sets the page shadow on the canvas
221 It was originally set to 5, which is really cheesy!
222 It now is an attribute in the document's namedview. If a value of
223 0 is used, then the constructor for a shadow is not initialized.
224 */
226 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
227 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
228 }
231 /* Connect event for page resize */
232 _doc2dt[5] = sp_document_height (document);
233 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
235 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
237 NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
238 SP_CANVAS_ARENA (drawing)->arena,
239 dkey,
240 SP_ITEM_SHOW_DISPLAY);
241 if (ai) {
242 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
243 nr_arena_item_unref (ai);
244 }
246 namedview->show(this);
247 /* Ugly hack */
248 activate_guides (true);
249 /* Ugly hack */
250 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
252 /* Set up notification of rebuilding the document, this allows
253 for saving object related settings in the document. */
254 _reconstruction_start_connection =
255 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
256 _reconstruction_finish_connection =
257 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
258 _reconstruction_old_layer_id = NULL;
260 _commit_connection = document->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
262 // ?
263 // sp_active_desktop_set (desktop);
264 _inkscape = INKSCAPE;
266 _activate_connection = _activate_signal.connect(
267 sigc::bind(
268 sigc::ptr_fun(_onActivate),
269 this
270 )
271 );
272 _deactivate_connection = _deactivate_signal.connect(
273 sigc::bind(
274 sigc::ptr_fun(_onDeactivate),
275 this
276 )
277 );
279 _sel_modified_connection = selection->connectModified(
280 sigc::bind(
281 sigc::ptr_fun(&_onSelectionModified),
282 this
283 )
284 );
285 _sel_changed_connection = selection->connectChanged(
286 sigc::bind(
287 sigc::ptr_fun(&_onSelectionChanged),
288 this
289 )
290 );
293 /* setup LayerManager */
294 // (Setting up after the connections are all in place, as it may use some of them)
295 layer_manager = new Inkscape::LayerManager( this );
296 }
299 void SPDesktop::destroy()
300 {
301 _activate_connection.disconnect();
302 _deactivate_connection.disconnect();
303 _sel_modified_connection.disconnect();
304 _sel_changed_connection.disconnect();
305 _modified_connection.disconnect();
306 _commit_connection.disconnect();
307 _reconstruction_start_connection.disconnect();
308 _reconstruction_finish_connection.disconnect();
310 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
311 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
312 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
314 while (event_context) {
315 SPEventContext *ec = event_context;
316 event_context = ec->next;
317 sp_event_context_finish (ec);
318 g_object_unref (G_OBJECT (ec));
319 }
321 if (_layer_hierarchy) {
322 delete _layer_hierarchy;
323 }
325 if (_inkscape) {
326 _inkscape = NULL;
327 }
329 if (drawing) {
330 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
331 drawing = NULL;
332 }
334 delete _guides_message_context;
335 _guides_message_context = NULL;
337 g_list_free (zooms_past);
338 g_list_free (zooms_future);
339 }
341 SPDesktop::~SPDesktop() {}
343 //--------------------------------------------------------------------
344 /* Public methods */
346 void SPDesktop::setDisplayModeNormal()
347 {
348 prefs_set_int_attribute("options.outlinemode", "value", 0);
349 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
350 canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
351 displayMode = RENDERMODE_NORMAL;
352 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
353 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
354 }
356 void SPDesktop::setDisplayModeOutline()
357 {
358 prefs_set_int_attribute("options.outlinemode", "value", 1);
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 (prefs_get_int_attribute("options.outlinemode", "value", prefs_get_int_attribute("options.startmode", "outline", 0)))
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::Rect const bbox = sp_item_bbox_desktop(item);
432 return viewport.contains(bbox);
433 }
435 ///
436 bool SPDesktop::itemIsHidden(SPItem const *item) const {
437 return item->isHidden(this->dkey);
438 }
440 /**
441 * Set activate property of desktop; emit signal if changed.
442 */
443 void
444 SPDesktop::set_active (bool new_active)
445 {
446 if (new_active != _active) {
447 _active = new_active;
448 if (new_active) {
449 _activate_signal.emit();
450 } else {
451 _deactivate_signal.emit();
452 }
453 }
454 }
456 /**
457 * Set activate status of current desktop's named view.
458 */
459 void
460 SPDesktop::activate_guides(bool activate)
461 {
462 guides_active = activate;
463 namedview->activateGuides(this, activate);
464 }
466 /**
467 * Make desktop switch documents.
468 */
469 void
470 SPDesktop::change_document (SPDocument *theDocument)
471 {
472 g_return_if_fail (theDocument != NULL);
474 /* unselect everything before switching documents */
475 selection->clear();
477 setDocument (theDocument);
478 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
479 _document_replaced_signal.emit (this, theDocument);
480 }
482 /**
483 * Make desktop switch event contexts.
484 */
485 void
486 SPDesktop::set_event_context (GtkType type, const gchar *config)
487 {
488 SPEventContext *ec;
489 while (event_context) {
490 ec = event_context;
491 sp_event_context_deactivate (ec);
492 event_context = ec->next;
493 sp_event_context_finish (ec);
494 g_object_unref (G_OBJECT (ec));
495 }
497 Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
498 ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
499 ec->next = event_context;
500 event_context = ec;
501 sp_event_context_activate (ec);
502 _event_context_changed_signal.emit (this, ec);
503 }
505 /**
506 * Push event context onto desktop's context stack.
507 */
508 void
509 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
510 {
511 SPEventContext *ref, *ec;
512 Inkscape::XML::Node *repr;
514 if (event_context && event_context->key == key) return;
515 ref = event_context;
516 while (ref && ref->next && ref->next->key != key) ref = ref->next;
517 if (ref && ref->next) {
518 ec = ref->next;
519 ref->next = ec->next;
520 sp_event_context_finish (ec);
521 g_object_unref (G_OBJECT (ec));
522 }
524 if (event_context) sp_event_context_deactivate (event_context);
525 repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
526 ec = sp_event_context_new (type, this, repr, key);
527 ec->next = event_context;
528 event_context = ec;
529 sp_event_context_activate (ec);
530 _event_context_changed_signal.emit (this, ec);
531 }
533 /**
534 * Sets the coordinate status to a given point
535 */
536 void
537 SPDesktop::set_coordinate_status (NR::Point p) {
538 _widget->setCoordinateStatus(p);
539 }
541 /**
542 * \see sp_document_item_from_list_at_point_bottom()
543 */
544 SPItem *
545 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
546 {
547 g_return_val_if_fail (doc() != NULL, NULL);
548 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
549 }
551 /**
552 * \see sp_document_item_at_point()
553 */
554 SPItem *
555 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
556 {
557 g_return_val_if_fail (doc() != NULL, NULL);
558 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
559 }
561 /**
562 * \see sp_document_group_at_point()
563 */
564 SPItem *
565 SPDesktop::group_at_point (NR::Point const p) const
566 {
567 g_return_val_if_fail (doc() != NULL, NULL);
568 return sp_document_group_at_point (doc(), dkey, p);
569 }
571 /**
572 * \brief Returns the mouse point in document coordinates; if mouse is
573 * outside the canvas, returns the center of canvas viewpoint
574 */
575 NR::Point
576 SPDesktop::point() const
577 {
578 NR::Point p = _widget->getPointer();
579 NR::Point pw = sp_canvas_window_to_world (canvas, p);
580 p = w2d(pw);
582 NR::Rect const r = canvas->getViewbox();
584 NR::Point r0 = w2d(r.min());
585 NR::Point r1 = w2d(r.max());
587 if (p[NR::X] >= r0[NR::X] &&
588 p[NR::X] <= r1[NR::X] &&
589 p[NR::Y] >= r1[NR::Y] &&
590 p[NR::Y] <= r0[NR::Y])
591 {
592 return p;
593 } else {
594 return (r0 + r1) / 2;
595 }
596 }
598 /**
599 * Put current zoom data in history list.
600 */
601 void
602 SPDesktop::push_current_zoom (GList **history)
603 {
604 NR::Rect const area = get_display_area();
606 NRRect *old_zoom = g_new(NRRect, 1);
607 old_zoom->x0 = area.min()[NR::X];
608 old_zoom->x1 = area.max()[NR::X];
609 old_zoom->y0 = area.min()[NR::Y];
610 old_zoom->y1 = area.max()[NR::Y];
611 if ( *history == NULL
612 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
613 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
614 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
615 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
616 {
617 *history = g_list_prepend (*history, old_zoom);
618 }
619 }
621 /**
622 * Set viewbox.
623 */
624 void
625 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
626 {
627 g_assert(_widget);
629 // save the zoom
630 if (log) {
631 push_current_zoom(&zooms_past);
632 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
633 g_list_free (zooms_future);
634 zooms_future = NULL;
635 }
637 double const cx = 0.5 * (x0 + x1);
638 double const cy = 0.5 * (y0 + y1);
640 NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
642 double scale = expansion(_d2w);
643 double newscale;
644 if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
645 newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
646 } else {
647 newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
648 }
650 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
652 int clear = FALSE;
653 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
654 /* Set zoom factors */
655 _d2w = NR::Matrix(NR::scale(newscale, -newscale));
656 _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
657 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
658 clear = TRUE;
659 }
661 /* Calculate top left corner */
662 x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
663 y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
665 /* Scroll */
666 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
668 _widget->updateRulers();
669 _widget->updateScrollbars(expansion(_d2w));
670 _widget->updateZoom();
671 }
673 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
674 {
675 set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
676 }
678 /**
679 * Return viewbox dimensions.
680 */
681 NR::Rect SPDesktop::get_display_area() const
682 {
683 NR::Rect const viewbox = canvas->getViewbox();
685 double const scale = _d2w[0];
687 return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
688 NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
689 }
691 /**
692 * Revert back to previous zoom if possible.
693 */
694 void
695 SPDesktop::prev_zoom()
696 {
697 if (zooms_past == NULL) {
698 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
699 return;
700 }
702 // push current zoom into forward zooms list
703 push_current_zoom (&zooms_future);
705 // restore previous zoom
706 set_display_area (((NRRect *) zooms_past->data)->x0,
707 ((NRRect *) zooms_past->data)->y0,
708 ((NRRect *) zooms_past->data)->x1,
709 ((NRRect *) zooms_past->data)->y1,
710 0, false);
712 // remove the just-added zoom from the past zooms list
713 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
714 }
716 /**
717 * Set zoom to next in list.
718 */
719 void
720 SPDesktop::next_zoom()
721 {
722 if (zooms_future == NULL) {
723 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
724 return;
725 }
727 // push current zoom into past zooms list
728 push_current_zoom (&zooms_past);
730 // restore next zoom
731 set_display_area (((NRRect *) zooms_future->data)->x0,
732 ((NRRect *) zooms_future->data)->y0,
733 ((NRRect *) zooms_future->data)->x1,
734 ((NRRect *) zooms_future->data)->y1,
735 0, false);
737 // remove the just-used zoom from the zooms_future list
738 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
739 }
741 /**
742 * Zoom to point with absolute zoom factor.
743 */
744 void
745 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
746 {
747 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
749 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
750 // this check prevents "sliding" when trying to zoom in at maximum zoom;
751 /// \todo someone please fix calculations properly and remove this hack
752 if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
753 return;
755 NR::Rect const viewbox = canvas->getViewbox();
757 double const width2 = viewbox.dimensions()[NR::X] / zoom;
758 double const height2 = viewbox.dimensions()[NR::Y] / zoom;
760 set_display_area(cx - px * width2,
761 cy - py * height2,
762 cx + (1 - px) * width2,
763 cy + (1 - py) * height2,
764 0.0);
765 }
767 /**
768 * Zoom to center with absolute zoom factor.
769 */
770 void
771 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
772 {
773 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
774 }
776 /**
777 * Zoom to point with relative zoom factor.
778 */
779 void
780 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
781 {
782 NR::Rect const area = get_display_area();
784 if (cx < area.min()[NR::X]) {
785 cx = area.min()[NR::X];
786 }
787 if (cx > area.max()[NR::X]) {
788 cx = area.max()[NR::X];
789 }
790 if (cy < area.min()[NR::Y]) {
791 cy = area.min()[NR::Y];
792 }
793 if (cy > area.max()[NR::Y]) {
794 cy = area.max()[NR::Y];
795 }
797 gdouble const scale = expansion(_d2w) * zoom;
798 double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
799 double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
801 zoom_absolute_keep_point(cx, cy, px, py, scale);
802 }
804 /**
805 * Zoom to center with relative zoom factor.
806 */
807 void
808 SPDesktop::zoom_relative (double cx, double cy, double zoom)
809 {
810 gdouble scale = expansion(_d2w) * zoom;
811 zoom_absolute (cx, cy, scale);
812 }
814 /**
815 * Set display area to origin and current document dimensions.
816 */
817 void
818 SPDesktop::zoom_page()
819 {
820 NR::Rect d(NR::Point(0, 0),
821 NR::Point(sp_document_width(doc()), sp_document_height(doc())));
823 if (d.dimensions()[NR::X] < 1.0 || d.dimensions()[NR::Y] < 1.0) {
824 return;
825 }
827 set_display_area(d, 10);
828 }
830 /**
831 * Set display area to current document width.
832 */
833 void
834 SPDesktop::zoom_page_width()
835 {
836 NR::Rect const a = get_display_area();
838 if (sp_document_width(doc()) < 1.0) {
839 return;
840 }
842 NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
843 NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
845 set_display_area(d, 10);
846 }
848 /**
849 * Zoom to selection.
850 */
851 void
852 SPDesktop::zoom_selection()
853 {
854 NR::Rect const d = selection->bounds();
856 if (d.dimensions()[NR::X] < 0.1 || d.dimensions()[NR::Y] < 0.1) {
857 return;
858 }
860 set_display_area(d, 10);
861 }
863 /**
864 * Tell widget to let zoom widget grab keyboard focus.
865 */
866 void
867 SPDesktop::zoom_grab_focus()
868 {
869 _widget->letZoomGrabFocus();
870 }
872 /**
873 * Zoom to whole drawing.
874 */
875 void
876 SPDesktop::zoom_drawing()
877 {
878 g_return_if_fail (doc() != NULL);
879 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
880 g_return_if_fail (docitem != NULL);
882 NR::Rect d = sp_item_bbox_desktop(docitem);
884 /* Note that the second condition here indicates that
885 ** there are no items in the drawing.
886 */
887 if ( d.dimensions()[NR::X] < 1.0 || d.dimensions()[NR::Y] < 1.0 ) {
888 return;
889 }
891 set_display_area(d, 10);
892 }
894 /**
895 * Scroll canvas by specific coordinate amount.
896 */
897 void
898 SPDesktop::scroll_world (double dx, double dy)
899 {
900 g_assert(_widget);
902 NR::Rect const viewbox = canvas->getViewbox();
904 sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE);
906 _widget->updateRulers();
907 _widget->updateScrollbars(expansion(_d2w));
908 }
910 bool
911 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
912 {
913 gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
915 // autoscrolldistance is in screen pixels, but the display area is in document units
916 autoscrolldistance /= expansion(_d2w);
917 NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
919 if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
920 !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y]) ) {
922 NR::Point const s_w( (*p) * _d2w );
924 gdouble x_to;
925 if ((*p)[NR::X] < dbox.min()[NR::X])
926 x_to = dbox.min()[NR::X];
927 else if ((*p)[NR::X] > dbox.max()[NR::X])
928 x_to = dbox.max()[NR::X];
929 else
930 x_to = (*p)[NR::X];
932 gdouble y_to;
933 if ((*p)[NR::Y] < dbox.min()[NR::Y])
934 y_to = dbox.min()[NR::Y];
935 else if ((*p)[NR::Y] > dbox.max()[NR::Y])
936 y_to = dbox.max()[NR::Y];
937 else
938 y_to = (*p)[NR::Y];
940 NR::Point const d_dt(x_to, y_to);
941 NR::Point const d_w( d_dt * _d2w );
942 NR::Point const moved_w( d_w - s_w );
944 if (autoscrollspeed == 0)
945 autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
947 if (autoscrollspeed != 0)
948 scroll_world (autoscrollspeed * moved_w);
950 return true;
951 }
952 return false;
953 }
955 void
956 SPDesktop::fullscreen()
957 {
958 _widget->setFullscreen();
959 }
961 void
962 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
963 {
964 _widget->getGeometry (x, y, w, h);
965 }
967 void
968 SPDesktop::setWindowPosition (NR::Point p)
969 {
970 _widget->setPosition (p);
971 }
973 void
974 SPDesktop::setWindowSize (gint w, gint h)
975 {
976 _widget->setSize (w, h);
977 }
979 void
980 SPDesktop::setWindowTransient (void *p, int transient_policy)
981 {
982 _widget->setTransient (p, transient_policy);
983 }
985 void
986 SPDesktop::presentWindow()
987 {
988 _widget->present();
989 }
991 bool
992 SPDesktop::warnDialog (gchar *text)
993 {
994 return _widget->warnDialog (text);
995 }
997 void
998 SPDesktop::toggleRulers()
999 {
1000 _widget->toggleRulers();
1001 }
1003 void
1004 SPDesktop::toggleScrollbars()
1005 {
1006 _widget->toggleScrollbars();
1007 }
1009 void
1010 SPDesktop::layoutWidget()
1011 {
1012 _widget->layout();
1013 }
1015 void
1016 SPDesktop::destroyWidget()
1017 {
1018 _widget->destroy();
1019 }
1021 bool
1022 SPDesktop::shutdown()
1023 {
1024 return _widget->shutdown();
1025 }
1027 void
1028 SPDesktop::setToolboxFocusTo (gchar const *label)
1029 {
1030 _widget->setToolboxFocusTo (label);
1031 }
1033 void
1034 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1035 {
1036 _widget->setToolboxAdjustmentValue (id, val);
1037 }
1039 bool
1040 SPDesktop::isToolboxButtonActive (gchar const *id)
1041 {
1042 return _widget->isToolboxButtonActive (id);
1043 }
1045 void
1046 SPDesktop::emitToolSubselectionChanged(gpointer data)
1047 {
1048 _tool_subselection_changed.emit(data);
1049 inkscape_subselection_changed (this);
1050 }
1052 void
1053 SPDesktop::updateNow()
1054 {
1055 sp_canvas_update_now(canvas);
1056 }
1058 void
1059 SPDesktop::enableInteraction()
1060 {
1061 _widget->enableInteraction();
1062 }
1064 void SPDesktop::disableInteraction()
1065 {
1066 _widget->disableInteraction();
1067 }
1069 //----------------------------------------------------------------------
1070 // Callback implementations. The virtual ones are connected by the view.
1072 void
1073 SPDesktop::onPositionSet (double x, double y)
1074 {
1075 _widget->viewSetPosition (NR::Point(x,y));
1076 }
1078 void
1079 SPDesktop::onResized (double x, double y)
1080 {
1081 // Nothing called here
1082 }
1084 /**
1085 * Redraw callback; queues Gtk redraw; connected by View.
1086 */
1087 void
1088 SPDesktop::onRedrawRequested ()
1089 {
1090 if (main) {
1091 _widget->requestCanvasUpdate();
1092 }
1093 }
1095 void
1096 SPDesktop::updateCanvasNow()
1097 {
1098 _widget->requestCanvasUpdateAndWait();
1099 }
1101 /**
1102 * Associate document with desktop.
1103 */
1104 /// \todo fixme: refactor SPDesktop::init to use setDocument
1105 void
1106 SPDesktop::setDocument (SPDocument *doc)
1107 {
1108 if (this->doc() && doc) {
1109 namedview->hide(this);
1110 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1111 }
1113 if (_layer_hierarchy) {
1114 _layer_hierarchy->clear();
1115 delete _layer_hierarchy;
1116 }
1117 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1118 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1119 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1120 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1121 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1123 /* setup EventLog */
1124 event_log = new Inkscape::EventLog(doc);
1125 doc->addUndoObserver(*event_log);
1127 _commit_connection.disconnect();
1128 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1130 /// \todo fixme: This condition exists to make sure the code
1131 /// inside is called only once on initialization. But there
1132 /// are surely more safe methods to accomplish this.
1133 if (drawing) {
1134 NRArenaItem *ai;
1136 namedview = sp_document_namedview (doc, NULL);
1137 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1138 number = namedview->getViewCount();
1140 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1141 SP_CANVAS_ARENA (drawing)->arena,
1142 dkey,
1143 SP_ITEM_SHOW_DISPLAY);
1144 if (ai) {
1145 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1146 nr_arena_item_unref (ai);
1147 }
1148 namedview->show(this);
1149 /* Ugly hack */
1150 activate_guides (true);
1151 /* Ugly hack */
1152 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1153 }
1155 _document_replaced_signal.emit (this, doc);
1157 View::setDocument (doc);
1158 }
1160 void
1161 SPDesktop::onStatusMessage
1162 (Inkscape::MessageType type, gchar const *message)
1163 {
1164 if (_widget) {
1165 _widget->setMessage(type, message);
1166 }
1167 }
1169 void
1170 SPDesktop::onDocumentURISet (gchar const* uri)
1171 {
1172 _widget->setTitle(uri);
1173 }
1175 /**
1176 * Resized callback.
1177 */
1178 void
1179 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1180 {
1181 _doc2dt[5] = height;
1182 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1183 NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1184 SP_CTRLRECT(page)->setRectangle(a);
1185 SP_CTRLRECT(page_border)->setRectangle(a);
1186 }
1189 void
1190 SPDesktop::_onActivate (SPDesktop* dt)
1191 {
1192 if (!dt->_widget) return;
1193 dt->_widget->activateDesktop();
1194 }
1196 void
1197 SPDesktop::_onDeactivate (SPDesktop* dt)
1198 {
1199 if (!dt->_widget) return;
1200 dt->_widget->deactivateDesktop();
1201 }
1203 void
1204 SPDesktop::_onSelectionModified
1205 (Inkscape::Selection *selection, guint flags, SPDesktop *dt)
1206 {
1207 if (!dt->_widget) return;
1208 dt->_widget->updateScrollbars (expansion(dt->_d2w));
1209 }
1211 static void
1212 _onSelectionChanged
1213 (Inkscape::Selection *selection, SPDesktop *desktop)
1214 {
1215 /** \todo
1216 * only change the layer for single selections, or what?
1217 * This seems reasonable -- for multiple selections there can be many
1218 * different layers involved.
1219 */
1220 SPItem *item=selection->singleItem();
1221 if (item) {
1222 SPObject *layer=desktop->layerForObject(item);
1223 if ( layer && layer != desktop->currentLayer() ) {
1224 desktop->setCurrentLayer(layer);
1225 }
1226 }
1227 }
1229 /**
1230 * Calls event handler of current event context.
1231 * \param arena Unused
1232 * \todo fixme
1233 */
1234 static gint
1235 _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1236 {
1237 if (ai) {
1238 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1239 return sp_event_context_item_handler (desktop->event_context, spi, event);
1240 } else {
1241 return sp_event_context_root_handler (desktop->event_context, event);
1242 }
1243 }
1245 static void
1246 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1247 g_return_if_fail(SP_IS_GROUP(layer));
1248 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1249 }
1251 /// Callback
1252 static void
1253 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1254 g_return_if_fail(SP_IS_GROUP(layer));
1255 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1256 }
1258 /// Callback
1259 static void
1260 _layer_hierarchy_changed(SPObject *top, SPObject *bottom,
1261 SPDesktop *desktop)
1262 {
1263 desktop->_layer_changed_signal.emit (bottom);
1264 }
1266 /// Called when document is starting to be rebuilt.
1267 static void
1268 _reconstruction_start (SPDesktop * desktop)
1269 {
1270 // printf("Desktop, starting reconstruction\n");
1271 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1272 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1274 /*
1275 GSList const * selection_objs = desktop->selection->list();
1276 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1278 }
1279 */
1280 desktop->selection->clear();
1282 // printf("Desktop, starting reconstruction end\n");
1283 }
1285 /// Called when document rebuild is finished.
1286 static void
1287 _reconstruction_finish (SPDesktop * desktop)
1288 {
1289 // printf("Desktop, finishing reconstruction\n");
1290 if (desktop->_reconstruction_old_layer_id == NULL)
1291 return;
1293 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1294 if (newLayer != NULL)
1295 desktop->setCurrentLayer(newLayer);
1297 g_free(desktop->_reconstruction_old_layer_id);
1298 desktop->_reconstruction_old_layer_id = NULL;
1299 // printf("Desktop, finishing reconstruction end\n");
1300 return;
1301 }
1303 /**
1304 * Namedview_modified callback.
1305 */
1306 static void
1307 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1308 {
1309 SPNamedView *nv=SP_NAMEDVIEW(obj);
1311 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1313 /* Recalculate snap distances */
1314 /* FIXME: why is the desktop getting involved in setting up something
1315 ** that is entirely to do with the namedview?
1316 */
1317 _update_snap_distances (desktop);
1319 /* Show/hide page background */
1320 if (nv->pagecolor & 0xff) {
1321 sp_canvas_item_show (desktop->table);
1322 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1323 sp_canvas_item_move_to_z (desktop->table, 0);
1324 } else {
1325 sp_canvas_item_hide (desktop->table);
1326 }
1328 /* Show/hide page border */
1329 if (nv->showborder) {
1330 // show
1331 sp_canvas_item_show (desktop->page_border);
1332 // set color and shadow
1333 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1334 if (nv->pageshadow) {
1335 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1336 }
1337 // place in the z-order stack
1338 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1339 sp_canvas_item_move_to_z (desktop->page_border, 2);
1340 } else {
1341 int order = sp_canvas_item_order (desktop->page_border);
1342 int morder = sp_canvas_item_order (desktop->drawing);
1343 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1344 morder - order);
1345 }
1346 } else {
1347 sp_canvas_item_hide (desktop->page_border);
1348 if (nv->pageshadow) {
1349 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1350 }
1351 }
1353 /* Show/hide page shadow */
1354 if (nv->showpageshadow && nv->pageshadow) {
1355 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1356 } else {
1357 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1358 }
1360 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1361 (SP_RGBA32_R_U(nv->pagecolor) +
1362 SP_RGBA32_G_U(nv->pagecolor) +
1363 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1364 // the background color is light or transparent, use black outline
1365 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1366 } else { // use white outline
1367 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1368 }
1369 }
1370 }
1372 /**
1373 * Callback to reset snapper's distances.
1374 */
1375 static void
1376 _update_snap_distances (SPDesktop *desktop)
1377 {
1378 SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1380 SPNamedView &nv = *desktop->namedview;
1382 nv.snap_manager.grid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1383 *nv.gridtoleranceunit,
1384 px));
1385 nv.snap_manager.axonomgrid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1386 *nv.gridtoleranceunit,
1387 px));
1388 nv.snap_manager.guide.setDistance(sp_convert_distance_full(nv.guidetolerance,
1389 *nv.guidetoleranceunit,
1390 px));
1391 nv.snap_manager.object.setDistance(sp_convert_distance_full(nv.objecttolerance,
1392 *nv.objecttoleranceunit,
1393 px));
1394 }
1397 NR::Matrix SPDesktop::w2d() const
1398 {
1399 return _w2d;
1400 }
1402 NR::Point SPDesktop::w2d(NR::Point const &p) const
1403 {
1404 return p * _w2d;
1405 }
1407 NR::Point SPDesktop::d2w(NR::Point const &p) const
1408 {
1409 return p * _d2w;
1410 }
1412 NR::Matrix SPDesktop::doc2dt() const
1413 {
1414 return _doc2dt;
1415 }
1417 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1418 {
1419 return p * _doc2dt;
1420 }
1422 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1423 {
1424 return p / _doc2dt;
1425 }
1428 /**
1429 * Pop event context from desktop's context stack. Never used.
1430 */
1431 // void
1432 // SPDesktop::pop_event_context (unsigned int key)
1433 // {
1434 // SPEventContext *ec = NULL;
1435 //
1436 // if (event_context && event_context->key == key) {
1437 // g_return_if_fail (event_context);
1438 // g_return_if_fail (event_context->next);
1439 // ec = event_context;
1440 // sp_event_context_deactivate (ec);
1441 // event_context = ec->next;
1442 // sp_event_context_activate (event_context);
1443 // _event_context_changed_signal.emit (this, ec);
1444 // }
1445 //
1446 // SPEventContext *ref = event_context;
1447 // while (ref && ref->next && ref->next->key != key)
1448 // ref = ref->next;
1449 //
1450 // if (ref && ref->next) {
1451 // ec = ref->next;
1452 // ref->next = ec->next;
1453 // }
1454 //
1455 // if (ec) {
1456 // sp_event_context_finish (ec);
1457 // g_object_unref (G_OBJECT (ec));
1458 // }
1459 // }
1461 /*
1462 Local Variables:
1463 mode:c++
1464 c-file-style:"stroustrup"
1465 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1466 indent-tabs-mode:nil
1467 fill-column:99
1468 End:
1469 */
1470 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :