1 #define __SP_DESKTOP_C__
3 /** \file
4 * Editable view implementation
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * MenTaLguY <mental@rydia.net>
9 * bulia byak <buliabyak@users.sf.net>
10 * Ralf Stephan <ralf@ark.in-berlin.de>
11 * John Bintz <jcoswell@coswellproductions.org>
12 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
13 *
14 * Copyright (C) 2006-2007 Johan Engelen
15 * Copyright (C) 2006 John Bintz
16 * Copyright (C) 2004 MenTaLguY
17 * Copyright (C) 1999-2002 Lauris Kaplinski
18 * Copyright (C) 2000-2001 Ximian, Inc.
19 *
20 * Released under GNU GPL, read the file 'COPYING' for more information
21 */
23 /** \class SPDesktop
24 * SPDesktop is a subclass of View, implementing an editable document
25 * canvas. It is extensively used by many UI controls that need certain
26 * visual representations of their own.
27 *
28 * SPDesktop provides a certain set of SPCanvasItems, serving as GUI
29 * layers of different control objects. The one containing the whole
30 * document is the drawing layer. In addition to it, there are grid,
31 * guide, sketch and control layers. The sketch layer is used for
32 * temporary drawing objects, before the real objects in document are
33 * created. The control layer contains editing knots, rubberband and
34 * similar non-document UI objects.
35 *
36 * Each SPDesktop is associated with a SPNamedView node of the document
37 * tree. Currently, all desktops are created from a single main named
38 * view, but in the future there may be support for different ones.
39 * SPNamedView serves as an in-document container for desktop-related
40 * data, like grid and guideline placement, snapping options and so on.
41 *
42 * Associated with each SPDesktop are the two most important editing
43 * related objects - SPSelection and SPEventContext.
44 *
45 * Sodipodi keeps track of the active desktop and invokes notification
46 * signals whenever it changes. UI elements can use these to update their
47 * display to the selection of the currently active editing window.
48 * (Lauris Kaplinski)
49 */
51 #ifdef HAVE_CONFIG_H
52 # include "config.h"
53 #endif
55 #include <glibmm/i18n.h>
56 #include <sigc++/functors/mem_fun.h>
57 #include <gtkmm.h>
59 #include "macros.h"
60 #include "inkscape-private.h"
61 #include "desktop.h"
62 #include "desktop-events.h"
63 #include "desktop-handles.h"
64 #include "document.h"
65 #include "message-stack.h"
66 #include "selection.h"
67 #include "select-context.h"
68 #include "sp-namedview.h"
69 #include "color.h"
70 #include "sp-item-group.h"
71 #include "prefs-utils.h"
72 #include "object-hierarchy.h"
73 #include "helper/units.h"
74 #include "display/canvas-arena.h"
75 #include "display/nr-arena.h"
76 #include "display/gnome-canvas-acetate.h"
77 #include "display/sodipodi-ctrlrect.h"
78 #include "display/sp-canvas-util.h"
79 #include "libnr/nr-matrix-div.h"
80 #include "libnr/nr-rect-ops.h"
81 #include "ui/dialog/dialog-manager.h"
82 #include "xml/repr.h"
83 #include "message-context.h"
84 #include "layer-manager.h"
85 #include "event-log.h"
86 #include "display/canvas-grid.h"
88 namespace Inkscape { namespace XML { class Node; }}
90 // Callback declarations
91 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
92 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
93 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
94 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
95 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
96 static void _reconstruction_start(SPDesktop * desktop);
97 static void _reconstruction_finish(SPDesktop * desktop);
98 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
99 static void _update_snap_distances (SPDesktop *desktop);
101 /**
102 * Return new desktop object.
103 * \pre namedview != NULL.
104 * \pre canvas != NULL.
105 */
106 SPDesktop::SPDesktop()
107 {
108 _dlg_mgr = NULL;
109 _widget = 0;
110 namedview = NULL;
111 selection = NULL;
112 acetate = NULL;
113 main = NULL;
114 grid = NULL;
115 guides = NULL;
116 drawing = NULL;
117 sketch = NULL;
118 controls = NULL;
119 event_context = 0;
120 layer_manager = 0;
122 _d2w.set_identity();
123 _w2d.set_identity();
124 _doc2dt = NR::Matrix(NR::scale(1, -1));
126 guides_active = false;
128 zooms_past = NULL;
129 zooms_future = NULL;
131 is_fullscreen = false;
132 waiting_cursor = false;
134 gr_item = NULL;
135 gr_point_type = 0;
136 gr_point_i = 0;
137 gr_fill_or_stroke = true;
139 _layer_hierarchy = NULL;
140 _active = false;
142 selection = Inkscape::GC::release (new Inkscape::Selection (this));
143 }
145 void
146 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
148 {
149 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
151 current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
153 namedview = nv;
154 canvas = aCanvas;
156 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
157 /* Kill flicker */
158 sp_document_ensure_up_to_date (document);
160 /* Setup Dialog Manager */
161 _dlg_mgr = new Inkscape::UI::Dialog::DialogManager();
163 dkey = sp_item_display_key_new (1);
165 /* Connect document */
166 setDocument (document);
168 number = namedview->getViewCount();
171 /* Setup Canvas */
172 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
174 SPCanvasGroup *root = sp_canvas_root (canvas);
176 /* Setup adminstrative layers */
177 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
178 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
179 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
180 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
182 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
183 SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
184 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
185 sp_canvas_item_move_to_z (table, 0);
187 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
188 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
189 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
191 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
192 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
194 SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
196 if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
197 // Start in outline mode
198 setDisplayModeOutline();
199 } else {
200 // Start in normal mode, default
201 setDisplayModeNormal();
202 }
204 grid = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
205 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
206 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
207 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
209 /* Push select tool to the bottom of stack */
210 /** \todo
211 * FIXME: this is the only call to this. Everything else seems to just
212 * call "set" instead of "push". Can we assume that there is only one
213 * context ever?
214 */
215 push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
217 // display rect and zoom are now handled in sp_desktop_widget_realize()
219 NR::Rect const d(NR::Point(0.0, 0.0),
220 NR::Point(sp_document_width(document), sp_document_height(document)));
222 SP_CTRLRECT(page)->setRectangle(d);
223 SP_CTRLRECT(page_border)->setRectangle(d);
225 /* the following sets the page shadow on the canvas
226 It was originally set to 5, which is really cheesy!
227 It now is an attribute in the document's namedview. If a value of
228 0 is used, then the constructor for a shadow is not initialized.
229 */
231 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
232 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
233 }
236 /* Connect event for page resize */
237 _doc2dt[5] = sp_document_height (document);
238 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
240 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
242 NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
243 SP_CANVAS_ARENA (drawing)->arena,
244 dkey,
245 SP_ITEM_SHOW_DISPLAY);
246 if (ai) {
247 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
248 nr_arena_item_unref (ai);
249 }
251 namedview->show(this);
252 /* Ugly hack */
253 activate_guides (true);
254 /* Ugly hack */
255 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
257 /* Set up notification of rebuilding the document, this allows
258 for saving object related settings in the document. */
259 _reconstruction_start_connection =
260 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
261 _reconstruction_finish_connection =
262 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
263 _reconstruction_old_layer_id = NULL;
265 _commit_connection = document->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
267 // ?
268 // sp_active_desktop_set (desktop);
269 _inkscape = INKSCAPE;
271 _activate_connection = _activate_signal.connect(
272 sigc::bind(
273 sigc::ptr_fun(_onActivate),
274 this
275 )
276 );
277 _deactivate_connection = _deactivate_signal.connect(
278 sigc::bind(
279 sigc::ptr_fun(_onDeactivate),
280 this
281 )
282 );
284 _sel_modified_connection = selection->connectModified(
285 sigc::bind(
286 sigc::ptr_fun(&_onSelectionModified),
287 this
288 )
289 );
290 _sel_changed_connection = selection->connectChanged(
291 sigc::bind(
292 sigc::ptr_fun(&_onSelectionChanged),
293 this
294 )
295 );
298 /* setup LayerManager */
299 // (Setting up after the connections are all in place, as it may use some of them)
300 layer_manager = new Inkscape::LayerManager( this );
301 }
304 void SPDesktop::destroy()
305 {
306 _activate_connection.disconnect();
307 _deactivate_connection.disconnect();
308 _sel_modified_connection.disconnect();
309 _sel_changed_connection.disconnect();
310 _modified_connection.disconnect();
311 _commit_connection.disconnect();
312 _reconstruction_start_connection.disconnect();
313 _reconstruction_finish_connection.disconnect();
315 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
316 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
317 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
319 while (event_context) {
320 SPEventContext *ec = event_context;
321 event_context = ec->next;
322 sp_event_context_finish (ec);
323 g_object_unref (G_OBJECT (ec));
324 }
326 if (_layer_hierarchy) {
327 delete _layer_hierarchy;
328 }
330 if (_inkscape) {
331 _inkscape = NULL;
332 }
334 if (drawing) {
335 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
336 drawing = NULL;
337 }
339 delete _guides_message_context;
340 _guides_message_context = NULL;
342 g_list_free (zooms_past);
343 g_list_free (zooms_future);
344 }
346 SPDesktop::~SPDesktop() {}
348 //--------------------------------------------------------------------
349 /* Public methods */
351 void SPDesktop::setDisplayModeNormal()
352 {
353 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
354 canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
355 displayMode = RENDERMODE_NORMAL;
356 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
357 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
358 }
360 void SPDesktop::setDisplayModeOutline()
361 {
362 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
363 canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
364 displayMode = RENDERMODE_OUTLINE;
365 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
366 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
367 }
369 void SPDesktop::displayModeToggle()
370 {
371 if (displayMode == RENDERMODE_OUTLINE)
372 setDisplayModeNormal();
373 else
374 setDisplayModeOutline();
375 }
377 /**
378 * Returns current root (=bottom) layer.
379 */
380 SPObject *SPDesktop::currentRoot() const
381 {
382 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
383 }
385 /**
386 * Returns current top layer.
387 */
388 SPObject *SPDesktop::currentLayer() const
389 {
390 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
391 }
393 /**
394 * Sets the current layer of the desktop.
395 *
396 * Make \a object the top layer.
397 */
398 void SPDesktop::setCurrentLayer(SPObject *object) {
399 g_return_if_fail(SP_IS_GROUP(object));
400 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
401 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
402 _layer_hierarchy->setBottom(object);
403 }
405 /**
406 * Return layer that contains \a object.
407 */
408 SPObject *SPDesktop::layerForObject(SPObject *object) {
409 g_return_val_if_fail(object != NULL, NULL);
411 SPObject *root=currentRoot();
412 object = SP_OBJECT_PARENT(object);
413 while ( object && object != root && !isLayer(object) ) {
414 object = SP_OBJECT_PARENT(object);
415 }
416 return object;
417 }
419 /**
420 * True if object is a layer.
421 */
422 bool SPDesktop::isLayer(SPObject *object) const {
423 return ( SP_IS_GROUP(object)
424 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
425 == SPGroup::LAYER ) );
426 }
428 /**
429 * True if desktop viewport fully contains \a item's bbox.
430 */
431 bool SPDesktop::isWithinViewport (SPItem *item) const
432 {
433 NR::Rect const viewport = get_display_area();
434 NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
435 if (bbox) {
436 return viewport.contains(*bbox);
437 } else {
438 return true;
439 }
440 }
442 ///
443 bool SPDesktop::itemIsHidden(SPItem const *item) const {
444 return item->isHidden(this->dkey);
445 }
447 /**
448 * Set activate property of desktop; emit signal if changed.
449 */
450 void
451 SPDesktop::set_active (bool new_active)
452 {
453 if (new_active != _active) {
454 _active = new_active;
455 if (new_active) {
456 _activate_signal.emit();
457 } else {
458 _deactivate_signal.emit();
459 }
460 }
461 }
463 /**
464 * Set activate status of current desktop's named view.
465 */
466 void
467 SPDesktop::activate_guides(bool activate)
468 {
469 guides_active = activate;
470 namedview->activateGuides(this, activate);
471 }
473 /**
474 * Make desktop switch documents.
475 */
476 void
477 SPDesktop::change_document (SPDocument *theDocument)
478 {
479 g_return_if_fail (theDocument != NULL);
481 /* unselect everything before switching documents */
482 selection->clear();
484 setDocument (theDocument);
485 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
486 _document_replaced_signal.emit (this, theDocument);
487 }
489 /**
490 * Make desktop switch event contexts.
491 */
492 void
493 SPDesktop::set_event_context (GtkType type, const gchar *config)
494 {
495 SPEventContext *ec;
496 while (event_context) {
497 ec = event_context;
498 sp_event_context_deactivate (ec);
499 event_context = ec->next;
500 sp_event_context_finish (ec);
501 g_object_unref (G_OBJECT (ec));
502 }
504 Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
505 ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
506 ec->next = event_context;
507 event_context = ec;
508 sp_event_context_activate (ec);
509 _event_context_changed_signal.emit (this, ec);
510 }
512 /**
513 * Push event context onto desktop's context stack.
514 */
515 void
516 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
517 {
518 SPEventContext *ref, *ec;
519 Inkscape::XML::Node *repr;
521 if (event_context && event_context->key == key) return;
522 ref = event_context;
523 while (ref && ref->next && ref->next->key != key) ref = ref->next;
524 if (ref && ref->next) {
525 ec = ref->next;
526 ref->next = ec->next;
527 sp_event_context_finish (ec);
528 g_object_unref (G_OBJECT (ec));
529 }
531 if (event_context) sp_event_context_deactivate (event_context);
532 repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
533 ec = sp_event_context_new (type, this, repr, key);
534 ec->next = event_context;
535 event_context = ec;
536 sp_event_context_activate (ec);
537 _event_context_changed_signal.emit (this, ec);
538 }
540 /**
541 * Sets the coordinate status to a given point
542 */
543 void
544 SPDesktop::set_coordinate_status (NR::Point p) {
545 _widget->setCoordinateStatus(p);
546 }
548 /**
549 * \see sp_document_item_from_list_at_point_bottom()
550 */
551 SPItem *
552 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
553 {
554 g_return_val_if_fail (doc() != NULL, NULL);
555 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
556 }
558 /**
559 * \see sp_document_item_at_point()
560 */
561 SPItem *
562 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
563 {
564 g_return_val_if_fail (doc() != NULL, NULL);
565 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
566 }
568 /**
569 * \see sp_document_group_at_point()
570 */
571 SPItem *
572 SPDesktop::group_at_point (NR::Point const p) const
573 {
574 g_return_val_if_fail (doc() != NULL, NULL);
575 return sp_document_group_at_point (doc(), dkey, p);
576 }
578 /**
579 * \brief Returns the mouse point in document coordinates; if mouse is
580 * outside the canvas, returns the center of canvas viewpoint
581 */
582 NR::Point
583 SPDesktop::point() const
584 {
585 NR::Point p = _widget->getPointer();
586 NR::Point pw = sp_canvas_window_to_world (canvas, p);
587 p = w2d(pw);
589 NR::Rect const r = canvas->getViewbox();
591 NR::Point r0 = w2d(r.min());
592 NR::Point r1 = w2d(r.max());
594 if (p[NR::X] >= r0[NR::X] &&
595 p[NR::X] <= r1[NR::X] &&
596 p[NR::Y] >= r1[NR::Y] &&
597 p[NR::Y] <= r0[NR::Y])
598 {
599 return p;
600 } else {
601 return (r0 + r1) / 2;
602 }
603 }
605 /**
606 * Put current zoom data in history list.
607 */
608 void
609 SPDesktop::push_current_zoom (GList **history)
610 {
611 NR::Rect const area = get_display_area();
613 NRRect *old_zoom = g_new(NRRect, 1);
614 old_zoom->x0 = area.min()[NR::X];
615 old_zoom->x1 = area.max()[NR::X];
616 old_zoom->y0 = area.min()[NR::Y];
617 old_zoom->y1 = area.max()[NR::Y];
618 if ( *history == NULL
619 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
620 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
621 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
622 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
623 {
624 *history = g_list_prepend (*history, old_zoom);
625 }
626 }
628 /**
629 * Set viewbox.
630 */
631 void
632 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
633 {
634 g_assert(_widget);
636 // save the zoom
637 if (log) {
638 push_current_zoom(&zooms_past);
639 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
640 g_list_free (zooms_future);
641 zooms_future = NULL;
642 }
644 double const cx = 0.5 * (x0 + x1);
645 double const cy = 0.5 * (y0 + y1);
647 NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
649 double scale = expansion(_d2w);
650 double newscale;
651 if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
652 newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
653 } else {
654 newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
655 }
657 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
659 int clear = FALSE;
660 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
661 /* Set zoom factors */
662 _d2w = NR::Matrix(NR::scale(newscale, -newscale));
663 _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
664 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
665 clear = TRUE;
666 }
668 /* Calculate top left corner */
669 x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
670 y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
672 /* Scroll */
673 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
675 _widget->updateRulers();
676 _widget->updateScrollbars(expansion(_d2w));
677 _widget->updateZoom();
678 }
680 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
681 {
682 set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
683 }
685 /**
686 * Return viewbox dimensions.
687 */
688 NR::Rect SPDesktop::get_display_area() const
689 {
690 NR::Rect const viewbox = canvas->getViewbox();
692 double const scale = _d2w[0];
694 return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
695 NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
696 }
698 /**
699 * Revert back to previous zoom if possible.
700 */
701 void
702 SPDesktop::prev_zoom()
703 {
704 if (zooms_past == NULL) {
705 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
706 return;
707 }
709 // push current zoom into forward zooms list
710 push_current_zoom (&zooms_future);
712 // restore previous zoom
713 set_display_area (((NRRect *) zooms_past->data)->x0,
714 ((NRRect *) zooms_past->data)->y0,
715 ((NRRect *) zooms_past->data)->x1,
716 ((NRRect *) zooms_past->data)->y1,
717 0, false);
719 // remove the just-added zoom from the past zooms list
720 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
721 }
723 /**
724 * Set zoom to next in list.
725 */
726 void
727 SPDesktop::next_zoom()
728 {
729 if (zooms_future == NULL) {
730 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
731 return;
732 }
734 // push current zoom into past zooms list
735 push_current_zoom (&zooms_past);
737 // restore next zoom
738 set_display_area (((NRRect *) zooms_future->data)->x0,
739 ((NRRect *) zooms_future->data)->y0,
740 ((NRRect *) zooms_future->data)->x1,
741 ((NRRect *) zooms_future->data)->y1,
742 0, false);
744 // remove the just-used zoom from the zooms_future list
745 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
746 }
748 /**
749 * Zoom to point with absolute zoom factor.
750 */
751 void
752 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
753 {
754 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
756 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
757 // this check prevents "sliding" when trying to zoom in at maximum zoom;
758 /// \todo someone please fix calculations properly and remove this hack
759 if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
760 return;
762 NR::Rect const viewbox = canvas->getViewbox();
764 double const width2 = viewbox.dimensions()[NR::X] / zoom;
765 double const height2 = viewbox.dimensions()[NR::Y] / zoom;
767 set_display_area(cx - px * width2,
768 cy - py * height2,
769 cx + (1 - px) * width2,
770 cy + (1 - py) * height2,
771 0.0);
772 }
774 /**
775 * Zoom to center with absolute zoom factor.
776 */
777 void
778 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
779 {
780 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
781 }
783 /**
784 * Zoom to point with relative zoom factor.
785 */
786 void
787 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
788 {
789 NR::Rect const area = get_display_area();
791 if (cx < area.min()[NR::X]) {
792 cx = area.min()[NR::X];
793 }
794 if (cx > area.max()[NR::X]) {
795 cx = area.max()[NR::X];
796 }
797 if (cy < area.min()[NR::Y]) {
798 cy = area.min()[NR::Y];
799 }
800 if (cy > area.max()[NR::Y]) {
801 cy = area.max()[NR::Y];
802 }
804 gdouble const scale = expansion(_d2w) * zoom;
805 double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
806 double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
808 zoom_absolute_keep_point(cx, cy, px, py, scale);
809 }
811 /**
812 * Zoom to center with relative zoom factor.
813 */
814 void
815 SPDesktop::zoom_relative (double cx, double cy, double zoom)
816 {
817 gdouble scale = expansion(_d2w) * zoom;
818 zoom_absolute (cx, cy, scale);
819 }
821 /**
822 * Set display area to origin and current document dimensions.
823 */
824 void
825 SPDesktop::zoom_page()
826 {
827 NR::Rect d(NR::Point(0, 0),
828 NR::Point(sp_document_width(doc()), sp_document_height(doc())));
830 if (d.isEmpty(1.0)) {
831 return;
832 }
834 set_display_area(d, 10);
835 }
837 /**
838 * Set display area to current document width.
839 */
840 void
841 SPDesktop::zoom_page_width()
842 {
843 NR::Rect const a = get_display_area();
845 if (sp_document_width(doc()) < 1.0) {
846 return;
847 }
849 NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
850 NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
852 set_display_area(d, 10);
853 }
855 /**
856 * Zoom to selection.
857 */
858 void
859 SPDesktop::zoom_selection()
860 {
861 NR::Maybe<NR::Rect> const d = selection->bounds();
863 if ( !d || d->isEmpty(0.1) ) {
864 return;
865 }
867 set_display_area(*d, 10);
868 }
870 /**
871 * Tell widget to let zoom widget grab keyboard focus.
872 */
873 void
874 SPDesktop::zoom_grab_focus()
875 {
876 _widget->letZoomGrabFocus();
877 }
879 /**
880 * Zoom to whole drawing.
881 */
882 void
883 SPDesktop::zoom_drawing()
884 {
885 g_return_if_fail (doc() != NULL);
886 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
887 g_return_if_fail (docitem != NULL);
889 NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
891 /* Note that the second condition here indicates that
892 ** there are no items in the drawing.
893 */
894 if ( !d || d->isEmpty(1.0) ) {
895 return;
896 }
898 set_display_area(*d, 10);
899 }
901 /**
902 * Scroll canvas by specific coordinate amount.
903 */
904 void
905 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
906 {
907 g_assert(_widget);
909 NR::Rect const viewbox = canvas->getViewbox();
911 sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
913 _widget->updateRulers();
914 _widget->updateScrollbars(expansion(_d2w));
915 }
917 bool
918 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
919 {
920 gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
922 // autoscrolldistance is in screen pixels, but the display area is in document units
923 autoscrolldistance /= expansion(_d2w);
924 NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
926 if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
927 !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y]) ) {
929 NR::Point const s_w( (*p) * _d2w );
931 gdouble x_to;
932 if ((*p)[NR::X] < dbox.min()[NR::X])
933 x_to = dbox.min()[NR::X];
934 else if ((*p)[NR::X] > dbox.max()[NR::X])
935 x_to = dbox.max()[NR::X];
936 else
937 x_to = (*p)[NR::X];
939 gdouble y_to;
940 if ((*p)[NR::Y] < dbox.min()[NR::Y])
941 y_to = dbox.min()[NR::Y];
942 else if ((*p)[NR::Y] > dbox.max()[NR::Y])
943 y_to = dbox.max()[NR::Y];
944 else
945 y_to = (*p)[NR::Y];
947 NR::Point const d_dt(x_to, y_to);
948 NR::Point const d_w( d_dt * _d2w );
949 NR::Point const moved_w( d_w - s_w );
951 if (autoscrollspeed == 0)
952 autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
954 if (autoscrollspeed != 0)
955 scroll_world (autoscrollspeed * moved_w);
957 return true;
958 }
959 return false;
960 }
962 void
963 SPDesktop::fullscreen()
964 {
965 _widget->setFullscreen();
966 }
968 void
969 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
970 {
971 _widget->getGeometry (x, y, w, h);
972 }
974 void
975 SPDesktop::setWindowPosition (NR::Point p)
976 {
977 _widget->setPosition (p);
978 }
980 void
981 SPDesktop::setWindowSize (gint w, gint h)
982 {
983 _widget->setSize (w, h);
984 }
986 void
987 SPDesktop::setWindowTransient (void *p, int transient_policy)
988 {
989 _widget->setTransient (p, transient_policy);
990 }
992 void
993 SPDesktop::presentWindow()
994 {
995 _widget->present();
996 }
998 bool
999 SPDesktop::warnDialog (gchar *text)
1000 {
1001 return _widget->warnDialog (text);
1002 }
1004 void
1005 SPDesktop::toggleRulers()
1006 {
1007 _widget->toggleRulers();
1008 }
1010 void
1011 SPDesktop::toggleScrollbars()
1012 {
1013 _widget->toggleScrollbars();
1014 }
1016 void
1017 SPDesktop::layoutWidget()
1018 {
1019 _widget->layout();
1020 }
1022 void
1023 SPDesktop::destroyWidget()
1024 {
1025 _widget->destroy();
1026 }
1028 bool
1029 SPDesktop::shutdown()
1030 {
1031 return _widget->shutdown();
1032 }
1034 void
1035 SPDesktop::setToolboxFocusTo (gchar const *label)
1036 {
1037 _widget->setToolboxFocusTo (label);
1038 }
1040 void
1041 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1042 {
1043 _widget->setToolboxAdjustmentValue (id, val);
1044 }
1046 bool
1047 SPDesktop::isToolboxButtonActive (gchar const *id)
1048 {
1049 return _widget->isToolboxButtonActive (id);
1050 }
1052 void
1053 SPDesktop::emitToolSubselectionChanged(gpointer data)
1054 {
1055 _tool_subselection_changed.emit(data);
1056 inkscape_subselection_changed (this);
1057 }
1059 void
1060 SPDesktop::updateNow()
1061 {
1062 sp_canvas_update_now(canvas);
1063 }
1065 void
1066 SPDesktop::enableInteraction()
1067 {
1068 _widget->enableInteraction();
1069 }
1071 void SPDesktop::disableInteraction()
1072 {
1073 _widget->disableInteraction();
1074 }
1076 void SPDesktop::setWaitingCursor()
1077 {
1078 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1079 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1080 gdk_cursor_unref(waiting);
1081 waiting_cursor = true;
1083 // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1084 // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1085 // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1086 // after the call to setWaitingCursor as it was before
1087 while( Gtk::Main::events_pending() )
1088 Gtk::Main::iteration();
1089 }
1091 void SPDesktop::clearWaitingCursor()
1092 {
1093 if (waiting_cursor)
1094 sp_event_context_update_cursor(sp_desktop_event_context(this));
1095 }
1098 //----------------------------------------------------------------------
1099 // Callback implementations. The virtual ones are connected by the view.
1101 void
1102 SPDesktop::onPositionSet (double x, double y)
1103 {
1104 _widget->viewSetPosition (NR::Point(x,y));
1105 }
1107 void
1108 SPDesktop::onResized (double x, double y)
1109 {
1110 // Nothing called here
1111 }
1113 /**
1114 * Redraw callback; queues Gtk redraw; connected by View.
1115 */
1116 void
1117 SPDesktop::onRedrawRequested ()
1118 {
1119 if (main) {
1120 _widget->requestCanvasUpdate();
1121 }
1122 }
1124 void
1125 SPDesktop::updateCanvasNow()
1126 {
1127 _widget->requestCanvasUpdateAndWait();
1128 }
1130 /**
1131 * Associate document with desktop.
1132 */
1133 /// \todo fixme: refactor SPDesktop::init to use setDocument
1134 void
1135 SPDesktop::setDocument (SPDocument *doc)
1136 {
1137 if (this->doc() && doc) {
1138 namedview->hide(this);
1139 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1140 }
1142 if (_layer_hierarchy) {
1143 _layer_hierarchy->clear();
1144 delete _layer_hierarchy;
1145 }
1146 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1147 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1148 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1149 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1150 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1152 /* setup EventLog */
1153 event_log = new Inkscape::EventLog(doc);
1154 doc->addUndoObserver(*event_log);
1156 _commit_connection.disconnect();
1157 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1159 /// \todo fixme: This condition exists to make sure the code
1160 /// inside is called only once on initialization. But there
1161 /// are surely more safe methods to accomplish this.
1162 if (drawing) {
1163 NRArenaItem *ai;
1165 namedview = sp_document_namedview (doc, NULL);
1166 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1167 number = namedview->getViewCount();
1169 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1170 SP_CANVAS_ARENA (drawing)->arena,
1171 dkey,
1172 SP_ITEM_SHOW_DISPLAY);
1173 if (ai) {
1174 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1175 nr_arena_item_unref (ai);
1176 }
1177 namedview->show(this);
1178 /* Ugly hack */
1179 activate_guides (true);
1180 /* Ugly hack */
1181 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1182 }
1184 _document_replaced_signal.emit (this, doc);
1186 View::setDocument (doc);
1187 }
1189 void
1190 SPDesktop::onStatusMessage
1191 (Inkscape::MessageType type, gchar const *message)
1192 {
1193 if (_widget) {
1194 _widget->setMessage(type, message);
1195 }
1196 }
1198 void
1199 SPDesktop::onDocumentURISet (gchar const* uri)
1200 {
1201 _widget->setTitle(uri);
1202 }
1204 /**
1205 * Resized callback.
1206 */
1207 void
1208 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1209 {
1210 _doc2dt[5] = height;
1211 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1212 NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1213 SP_CTRLRECT(page)->setRectangle(a);
1214 SP_CTRLRECT(page_border)->setRectangle(a);
1215 }
1218 void
1219 SPDesktop::_onActivate (SPDesktop* dt)
1220 {
1221 if (!dt->_widget) return;
1222 dt->_widget->activateDesktop();
1223 }
1225 void
1226 SPDesktop::_onDeactivate (SPDesktop* dt)
1227 {
1228 if (!dt->_widget) return;
1229 dt->_widget->deactivateDesktop();
1230 }
1232 void
1233 SPDesktop::_onSelectionModified
1234 (Inkscape::Selection *selection, guint flags, SPDesktop *dt)
1235 {
1236 if (!dt->_widget) return;
1237 dt->_widget->updateScrollbars (expansion(dt->_d2w));
1238 }
1240 static void
1241 _onSelectionChanged
1242 (Inkscape::Selection *selection, SPDesktop *desktop)
1243 {
1244 /** \todo
1245 * only change the layer for single selections, or what?
1246 * This seems reasonable -- for multiple selections there can be many
1247 * different layers involved.
1248 */
1249 SPItem *item=selection->singleItem();
1250 if (item) {
1251 SPObject *layer=desktop->layerForObject(item);
1252 if ( layer && layer != desktop->currentLayer() ) {
1253 desktop->setCurrentLayer(layer);
1254 }
1255 }
1256 }
1258 /**
1259 * Calls event handler of current event context.
1260 * \param arena Unused
1261 * \todo fixme
1262 */
1263 static gint
1264 _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1265 {
1266 if (ai) {
1267 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1268 return sp_event_context_item_handler (desktop->event_context, spi, event);
1269 } else {
1270 return sp_event_context_root_handler (desktop->event_context, event);
1271 }
1272 }
1274 static void
1275 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1276 g_return_if_fail(SP_IS_GROUP(layer));
1277 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1278 }
1280 /// Callback
1281 static void
1282 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1283 g_return_if_fail(SP_IS_GROUP(layer));
1284 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1285 }
1287 /// Callback
1288 static void
1289 _layer_hierarchy_changed(SPObject *top, SPObject *bottom,
1290 SPDesktop *desktop)
1291 {
1292 desktop->_layer_changed_signal.emit (bottom);
1293 }
1295 /// Called when document is starting to be rebuilt.
1296 static void
1297 _reconstruction_start (SPDesktop * desktop)
1298 {
1299 // printf("Desktop, starting reconstruction\n");
1300 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1301 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1303 /*
1304 GSList const * selection_objs = desktop->selection->list();
1305 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1307 }
1308 */
1309 desktop->selection->clear();
1311 // printf("Desktop, starting reconstruction end\n");
1312 }
1314 /// Called when document rebuild is finished.
1315 static void
1316 _reconstruction_finish (SPDesktop * desktop)
1317 {
1318 // printf("Desktop, finishing reconstruction\n");
1319 if (desktop->_reconstruction_old_layer_id == NULL)
1320 return;
1322 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1323 if (newLayer != NULL)
1324 desktop->setCurrentLayer(newLayer);
1326 g_free(desktop->_reconstruction_old_layer_id);
1327 desktop->_reconstruction_old_layer_id = NULL;
1328 // printf("Desktop, finishing reconstruction end\n");
1329 return;
1330 }
1332 /**
1333 * Namedview_modified callback.
1334 */
1335 static void
1336 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1337 {
1338 SPNamedView *nv=SP_NAMEDVIEW(obj);
1340 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1342 /* Recalculate snap distances */
1343 /* FIXME: why is the desktop getting involved in setting up something
1344 ** that is entirely to do with the namedview?
1345 */
1346 _update_snap_distances (desktop);
1348 /* Show/hide page background */
1349 if (nv->pagecolor & 0xff) {
1350 sp_canvas_item_show (desktop->table);
1351 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1352 sp_canvas_item_move_to_z (desktop->table, 0);
1353 } else {
1354 sp_canvas_item_hide (desktop->table);
1355 }
1357 /* Show/hide page border */
1358 if (nv->showborder) {
1359 // show
1360 sp_canvas_item_show (desktop->page_border);
1361 // set color and shadow
1362 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1363 if (nv->pageshadow) {
1364 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1365 }
1366 // place in the z-order stack
1367 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1368 sp_canvas_item_move_to_z (desktop->page_border, 2);
1369 } else {
1370 int order = sp_canvas_item_order (desktop->page_border);
1371 int morder = sp_canvas_item_order (desktop->drawing);
1372 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1373 morder - order);
1374 }
1375 } else {
1376 sp_canvas_item_hide (desktop->page_border);
1377 if (nv->pageshadow) {
1378 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1379 }
1380 }
1382 /* Show/hide page shadow */
1383 if (nv->showpageshadow && nv->pageshadow) {
1384 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1385 } else {
1386 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1387 }
1389 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1390 (SP_RGBA32_R_U(nv->pagecolor) +
1391 SP_RGBA32_G_U(nv->pagecolor) +
1392 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1393 // the background color is light or transparent, use black outline
1394 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1395 } else { // use white outline
1396 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1397 }
1398 }
1399 }
1401 /**
1402 * Callback to reset snapper's distances.
1403 */
1404 static void
1405 _update_snap_distances (SPDesktop *desktop)
1406 {
1407 SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1409 SPNamedView &nv = *desktop->namedview;
1412 // FIXME GRID: make one gridsnapper object that snaps to all enabled grids by calling their snappers.
1413 nv.snap_manager.grid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1414 *nv.gridtoleranceunit,
1415 px));
1416 nv.snap_manager.axonomgrid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1417 *nv.gridtoleranceunit,
1418 px));
1419 //new grid snappers
1420 for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1421 Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1422 grid->snapper->setDistance(sp_convert_distance_full(nv.gridtolerance,
1423 *nv.gridtoleranceunit,
1424 px));
1425 }
1427 nv.snap_manager.guide.setDistance(sp_convert_distance_full(nv.guidetolerance,
1428 *nv.guidetoleranceunit,
1429 px));
1430 nv.snap_manager.object.setDistance(sp_convert_distance_full(nv.objecttolerance,
1431 *nv.objecttoleranceunit,
1432 px));
1433 }
1436 NR::Matrix SPDesktop::w2d() const
1437 {
1438 return _w2d;
1439 }
1441 NR::Point SPDesktop::w2d(NR::Point const &p) const
1442 {
1443 return p * _w2d;
1444 }
1446 NR::Point SPDesktop::d2w(NR::Point const &p) const
1447 {
1448 return p * _d2w;
1449 }
1451 NR::Matrix SPDesktop::doc2dt() const
1452 {
1453 return _doc2dt;
1454 }
1456 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1457 {
1458 return p * _doc2dt;
1459 }
1461 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1462 {
1463 return p / _doc2dt;
1464 }
1467 /**
1468 * Pop event context from desktop's context stack. Never used.
1469 */
1470 // void
1471 // SPDesktop::pop_event_context (unsigned int key)
1472 // {
1473 // SPEventContext *ec = NULL;
1474 //
1475 // if (event_context && event_context->key == key) {
1476 // g_return_if_fail (event_context);
1477 // g_return_if_fail (event_context->next);
1478 // ec = event_context;
1479 // sp_event_context_deactivate (ec);
1480 // event_context = ec->next;
1481 // sp_event_context_activate (event_context);
1482 // _event_context_changed_signal.emit (this, ec);
1483 // }
1484 //
1485 // SPEventContext *ref = event_context;
1486 // while (ref && ref->next && ref->next->key != key)
1487 // ref = ref->next;
1488 //
1489 // if (ref && ref->next) {
1490 // ec = ref->next;
1491 // ref->next = ec->next;
1492 // }
1493 //
1494 // if (ec) {
1495 // sp_event_context_finish (ec);
1496 // g_object_unref (G_OBJECT (ec));
1497 // }
1498 // }
1500 /*
1501 Local Variables:
1502 mode:c++
1503 c-file-style:"stroustrup"
1504 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1505 indent-tabs-mode:nil
1506 fill-column:99
1507 End:
1508 */
1509 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :