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 #include "display/sp-canvas.h"
90 namespace Inkscape { namespace XML { class Node; }}
92 // Callback declarations
93 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
94 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
95 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
96 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
97 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
98 static void _reconstruction_start(SPDesktop * desktop);
99 static void _reconstruction_finish(SPDesktop * desktop);
100 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
101 static void _update_snap_distances (SPDesktop *desktop);
103 /**
104 * Return new desktop object.
105 * \pre namedview != NULL.
106 * \pre canvas != NULL.
107 */
108 SPDesktop::SPDesktop()
109 {
110 _dlg_mgr = NULL;
111 _widget = 0;
112 namedview = NULL;
113 selection = NULL;
114 acetate = NULL;
115 main = NULL;
116 gridgroup = NULL;
117 guides = NULL;
118 drawing = NULL;
119 sketch = NULL;
120 controls = NULL;
121 event_context = 0;
122 layer_manager = 0;
124 _d2w.set_identity();
125 _w2d.set_identity();
126 _doc2dt = NR::Matrix(NR::scale(1, -1));
128 guides_active = false;
130 zooms_past = NULL;
131 zooms_future = NULL;
133 is_fullscreen = false;
134 waiting_cursor = false;
136 gr_item = NULL;
137 gr_point_type = 0;
138 gr_point_i = 0;
139 gr_fill_or_stroke = true;
141 _layer_hierarchy = NULL;
142 _active = false;
144 selection = Inkscape::GC::release (new Inkscape::Selection (this));
145 }
147 void
148 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
150 {
151 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
153 current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
155 namedview = nv;
156 canvas = aCanvas;
158 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
159 /* Kill flicker */
160 sp_document_ensure_up_to_date (document);
162 /* Setup Dialog Manager */
163 _dlg_mgr = new Inkscape::UI::Dialog::DialogManager();
165 dkey = sp_item_display_key_new (1);
167 /* Connect document */
168 setDocument (document);
170 number = namedview->getViewCount();
173 /* Setup Canvas */
174 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
176 SPCanvasGroup *root = sp_canvas_root (canvas);
178 /* Setup adminstrative layers */
179 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
180 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
181 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
182 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
184 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
185 SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
186 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
187 sp_canvas_item_move_to_z (table, 0);
189 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
190 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
191 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
193 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
194 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
196 SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
198 if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
199 // Start in outline mode
200 setDisplayModeOutline();
201 } else {
202 // Start in normal mode, default
203 setDisplayModeNormal();
204 }
206 gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
207 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
208 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
209 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
211 /* Push select tool to the bottom of stack */
212 /** \todo
213 * FIXME: this is the only call to this. Everything else seems to just
214 * call "set" instead of "push". Can we assume that there is only one
215 * context ever?
216 */
217 push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
219 // display rect and zoom are now handled in sp_desktop_widget_realize()
221 NR::Rect const d(NR::Point(0.0, 0.0),
222 NR::Point(sp_document_width(document), sp_document_height(document)));
224 SP_CTRLRECT(page)->setRectangle(d);
225 SP_CTRLRECT(page_border)->setRectangle(d);
227 /* the following sets the page shadow on the canvas
228 It was originally set to 5, which is really cheesy!
229 It now is an attribute in the document's namedview. If a value of
230 0 is used, then the constructor for a shadow is not initialized.
231 */
233 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
234 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
235 }
238 /* Connect event for page resize */
239 _doc2dt[5] = sp_document_height (document);
240 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
242 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
244 NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
245 SP_CANVAS_ARENA (drawing)->arena,
246 dkey,
247 SP_ITEM_SHOW_DISPLAY);
248 if (ai) {
249 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
250 nr_arena_item_unref (ai);
251 }
253 namedview->show(this);
254 /* Ugly hack */
255 activate_guides (true);
256 /* Ugly hack */
257 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
259 /* Set up notification of rebuilding the document, this allows
260 for saving object related settings in the document. */
261 _reconstruction_start_connection =
262 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
263 _reconstruction_finish_connection =
264 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
265 _reconstruction_old_layer_id = NULL;
267 _commit_connection = document->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
269 // ?
270 // sp_active_desktop_set (desktop);
271 _inkscape = INKSCAPE;
273 _activate_connection = _activate_signal.connect(
274 sigc::bind(
275 sigc::ptr_fun(_onActivate),
276 this
277 )
278 );
279 _deactivate_connection = _deactivate_signal.connect(
280 sigc::bind(
281 sigc::ptr_fun(_onDeactivate),
282 this
283 )
284 );
286 _sel_modified_connection = selection->connectModified(
287 sigc::bind(
288 sigc::ptr_fun(&_onSelectionModified),
289 this
290 )
291 );
292 _sel_changed_connection = selection->connectChanged(
293 sigc::bind(
294 sigc::ptr_fun(&_onSelectionChanged),
295 this
296 )
297 );
300 /* setup LayerManager */
301 // (Setting up after the connections are all in place, as it may use some of them)
302 layer_manager = new Inkscape::LayerManager( this );
304 grids_visible = true;
305 }
308 void SPDesktop::destroy()
309 {
310 _activate_connection.disconnect();
311 _deactivate_connection.disconnect();
312 _sel_modified_connection.disconnect();
313 _sel_changed_connection.disconnect();
314 _modified_connection.disconnect();
315 _commit_connection.disconnect();
316 _reconstruction_start_connection.disconnect();
317 _reconstruction_finish_connection.disconnect();
319 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
320 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
321 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
323 while (event_context) {
324 SPEventContext *ec = event_context;
325 event_context = ec->next;
326 sp_event_context_finish (ec);
327 g_object_unref (G_OBJECT (ec));
328 }
330 if (_layer_hierarchy) {
331 delete _layer_hierarchy;
332 }
334 if (_inkscape) {
335 _inkscape = NULL;
336 }
338 if (drawing) {
339 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
340 drawing = NULL;
341 }
343 delete _guides_message_context;
344 _guides_message_context = NULL;
346 g_list_free (zooms_past);
347 g_list_free (zooms_future);
348 }
350 SPDesktop::~SPDesktop() {}
352 //--------------------------------------------------------------------
353 /* Public methods */
355 void SPDesktop::setDisplayModeNormal()
356 {
357 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
358 canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
359 displayMode = RENDERMODE_NORMAL;
360 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
361 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
362 }
364 void SPDesktop::setDisplayModeOutline()
365 {
366 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
367 canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
368 displayMode = RENDERMODE_OUTLINE;
369 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
370 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
371 }
373 void SPDesktop::displayModeToggle()
374 {
375 if (displayMode == RENDERMODE_OUTLINE)
376 setDisplayModeNormal();
377 else
378 setDisplayModeOutline();
379 }
381 /**
382 * Returns current root (=bottom) layer.
383 */
384 SPObject *SPDesktop::currentRoot() const
385 {
386 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
387 }
389 /**
390 * Returns current top layer.
391 */
392 SPObject *SPDesktop::currentLayer() const
393 {
394 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
395 }
397 /**
398 * Sets the current layer of the desktop.
399 *
400 * Make \a object the top layer.
401 */
402 void SPDesktop::setCurrentLayer(SPObject *object) {
403 g_return_if_fail(SP_IS_GROUP(object));
404 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
405 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
406 _layer_hierarchy->setBottom(object);
407 }
409 /**
410 * Return layer that contains \a object.
411 */
412 SPObject *SPDesktop::layerForObject(SPObject *object) {
413 g_return_val_if_fail(object != NULL, NULL);
415 SPObject *root=currentRoot();
416 object = SP_OBJECT_PARENT(object);
417 while ( object && object != root && !isLayer(object) ) {
418 object = SP_OBJECT_PARENT(object);
419 }
420 return object;
421 }
423 /**
424 * True if object is a layer.
425 */
426 bool SPDesktop::isLayer(SPObject *object) const {
427 return ( SP_IS_GROUP(object)
428 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
429 == SPGroup::LAYER ) );
430 }
432 /**
433 * True if desktop viewport fully contains \a item's bbox.
434 */
435 bool SPDesktop::isWithinViewport (SPItem *item) const
436 {
437 NR::Rect const viewport = get_display_area();
438 NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
439 if (bbox) {
440 return viewport.contains(*bbox);
441 } else {
442 return true;
443 }
444 }
446 ///
447 bool SPDesktop::itemIsHidden(SPItem const *item) const {
448 return item->isHidden(this->dkey);
449 }
451 /**
452 * Set activate property of desktop; emit signal if changed.
453 */
454 void
455 SPDesktop::set_active (bool new_active)
456 {
457 if (new_active != _active) {
458 _active = new_active;
459 if (new_active) {
460 _activate_signal.emit();
461 } else {
462 _deactivate_signal.emit();
463 }
464 }
465 }
467 /**
468 * Set activate status of current desktop's named view.
469 */
470 void
471 SPDesktop::activate_guides(bool activate)
472 {
473 guides_active = activate;
474 namedview->activateGuides(this, activate);
475 }
477 /**
478 * Make desktop switch documents.
479 */
480 void
481 SPDesktop::change_document (SPDocument *theDocument)
482 {
483 g_return_if_fail (theDocument != NULL);
485 /* unselect everything before switching documents */
486 selection->clear();
488 setDocument (theDocument);
489 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
490 _document_replaced_signal.emit (this, theDocument);
491 }
493 /**
494 * Make desktop switch event contexts.
495 */
496 void
497 SPDesktop::set_event_context (GtkType type, const gchar *config)
498 {
499 SPEventContext *ec;
500 while (event_context) {
501 ec = event_context;
502 sp_event_context_deactivate (ec);
503 event_context = ec->next;
504 sp_event_context_finish (ec);
505 g_object_unref (G_OBJECT (ec));
506 }
508 Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
509 ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
510 ec->next = event_context;
511 event_context = ec;
512 sp_event_context_activate (ec);
513 _event_context_changed_signal.emit (this, ec);
514 }
516 /**
517 * Push event context onto desktop's context stack.
518 */
519 void
520 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
521 {
522 SPEventContext *ref, *ec;
523 Inkscape::XML::Node *repr;
525 if (event_context && event_context->key == key) return;
526 ref = event_context;
527 while (ref && ref->next && ref->next->key != key) ref = ref->next;
528 if (ref && ref->next) {
529 ec = ref->next;
530 ref->next = ec->next;
531 sp_event_context_finish (ec);
532 g_object_unref (G_OBJECT (ec));
533 }
535 if (event_context) sp_event_context_deactivate (event_context);
536 repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
537 ec = sp_event_context_new (type, this, repr, key);
538 ec->next = event_context;
539 event_context = ec;
540 sp_event_context_activate (ec);
541 _event_context_changed_signal.emit (this, ec);
542 }
544 /**
545 * Sets the coordinate status to a given point
546 */
547 void
548 SPDesktop::set_coordinate_status (NR::Point p) {
549 _widget->setCoordinateStatus(p);
550 }
552 /**
553 * \see sp_document_item_from_list_at_point_bottom()
554 */
555 SPItem *
556 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
557 {
558 g_return_val_if_fail (doc() != NULL, NULL);
559 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
560 }
562 /**
563 * \see sp_document_item_at_point()
564 */
565 SPItem *
566 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
567 {
568 g_return_val_if_fail (doc() != NULL, NULL);
569 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
570 }
572 /**
573 * \see sp_document_group_at_point()
574 */
575 SPItem *
576 SPDesktop::group_at_point (NR::Point const p) const
577 {
578 g_return_val_if_fail (doc() != NULL, NULL);
579 return sp_document_group_at_point (doc(), dkey, p);
580 }
582 /**
583 * \brief Returns the mouse point in document coordinates; if mouse is
584 * outside the canvas, returns the center of canvas viewpoint
585 */
586 NR::Point
587 SPDesktop::point() const
588 {
589 NR::Point p = _widget->getPointer();
590 NR::Point pw = sp_canvas_window_to_world (canvas, p);
591 p = w2d(pw);
593 NR::Rect const r = canvas->getViewbox();
595 NR::Point r0 = w2d(r.min());
596 NR::Point r1 = w2d(r.max());
598 if (p[NR::X] >= r0[NR::X] &&
599 p[NR::X] <= r1[NR::X] &&
600 p[NR::Y] >= r1[NR::Y] &&
601 p[NR::Y] <= r0[NR::Y])
602 {
603 return p;
604 } else {
605 return (r0 + r1) / 2;
606 }
607 }
609 /**
610 * Put current zoom data in history list.
611 */
612 void
613 SPDesktop::push_current_zoom (GList **history)
614 {
615 NR::Rect const area = get_display_area();
617 NRRect *old_zoom = g_new(NRRect, 1);
618 old_zoom->x0 = area.min()[NR::X];
619 old_zoom->x1 = area.max()[NR::X];
620 old_zoom->y0 = area.min()[NR::Y];
621 old_zoom->y1 = area.max()[NR::Y];
622 if ( *history == NULL
623 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
624 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
625 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
626 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
627 {
628 *history = g_list_prepend (*history, old_zoom);
629 }
630 }
632 /**
633 * Set viewbox.
634 */
635 void
636 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
637 {
638 g_assert(_widget);
640 // save the zoom
641 if (log) {
642 push_current_zoom(&zooms_past);
643 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
644 g_list_free (zooms_future);
645 zooms_future = NULL;
646 }
648 double const cx = 0.5 * (x0 + x1);
649 double const cy = 0.5 * (y0 + y1);
651 NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
653 double scale = expansion(_d2w);
654 double newscale;
655 if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
656 newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
657 } else {
658 newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
659 }
661 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
663 int clear = FALSE;
664 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
665 /* Set zoom factors */
666 _d2w = NR::Matrix(NR::scale(newscale, -newscale));
667 _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
668 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
669 clear = TRUE;
670 }
672 /* Calculate top left corner */
673 x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
674 y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
676 /* Scroll */
677 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
679 _widget->updateRulers();
680 _widget->updateScrollbars(expansion(_d2w));
681 _widget->updateZoom();
682 }
684 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
685 {
686 set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
687 }
689 /**
690 * Return viewbox dimensions.
691 */
692 NR::Rect SPDesktop::get_display_area() const
693 {
694 NR::Rect const viewbox = canvas->getViewbox();
696 double const scale = _d2w[0];
698 return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
699 NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
700 }
702 /**
703 * Revert back to previous zoom if possible.
704 */
705 void
706 SPDesktop::prev_zoom()
707 {
708 if (zooms_past == NULL) {
709 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
710 return;
711 }
713 // push current zoom into forward zooms list
714 push_current_zoom (&zooms_future);
716 // restore previous zoom
717 set_display_area (((NRRect *) zooms_past->data)->x0,
718 ((NRRect *) zooms_past->data)->y0,
719 ((NRRect *) zooms_past->data)->x1,
720 ((NRRect *) zooms_past->data)->y1,
721 0, false);
723 // remove the just-added zoom from the past zooms list
724 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
725 }
727 /**
728 * Set zoom to next in list.
729 */
730 void
731 SPDesktop::next_zoom()
732 {
733 if (zooms_future == NULL) {
734 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
735 return;
736 }
738 // push current zoom into past zooms list
739 push_current_zoom (&zooms_past);
741 // restore next zoom
742 set_display_area (((NRRect *) zooms_future->data)->x0,
743 ((NRRect *) zooms_future->data)->y0,
744 ((NRRect *) zooms_future->data)->x1,
745 ((NRRect *) zooms_future->data)->y1,
746 0, false);
748 // remove the just-used zoom from the zooms_future list
749 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
750 }
752 /**
753 * Zoom to point with absolute zoom factor.
754 */
755 void
756 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
757 {
758 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
760 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
761 // this check prevents "sliding" when trying to zoom in at maximum zoom;
762 /// \todo someone please fix calculations properly and remove this hack
763 if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
764 return;
766 NR::Rect const viewbox = canvas->getViewbox();
768 double const width2 = viewbox.dimensions()[NR::X] / zoom;
769 double const height2 = viewbox.dimensions()[NR::Y] / zoom;
771 set_display_area(cx - px * width2,
772 cy - py * height2,
773 cx + (1 - px) * width2,
774 cy + (1 - py) * height2,
775 0.0);
776 }
778 /**
779 * Zoom to center with absolute zoom factor.
780 */
781 void
782 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
783 {
784 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
785 }
787 /**
788 * Zoom to point with relative zoom factor.
789 */
790 void
791 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
792 {
793 NR::Rect const area = get_display_area();
795 if (cx < area.min()[NR::X]) {
796 cx = area.min()[NR::X];
797 }
798 if (cx > area.max()[NR::X]) {
799 cx = area.max()[NR::X];
800 }
801 if (cy < area.min()[NR::Y]) {
802 cy = area.min()[NR::Y];
803 }
804 if (cy > area.max()[NR::Y]) {
805 cy = area.max()[NR::Y];
806 }
808 gdouble const scale = expansion(_d2w) * zoom;
809 double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
810 double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
812 zoom_absolute_keep_point(cx, cy, px, py, scale);
813 }
815 /**
816 * Zoom to center with relative zoom factor.
817 */
818 void
819 SPDesktop::zoom_relative (double cx, double cy, double zoom)
820 {
821 gdouble scale = expansion(_d2w) * zoom;
822 zoom_absolute (cx, cy, scale);
823 }
825 /**
826 * Set display area to origin and current document dimensions.
827 */
828 void
829 SPDesktop::zoom_page()
830 {
831 NR::Rect d(NR::Point(0, 0),
832 NR::Point(sp_document_width(doc()), sp_document_height(doc())));
834 if (d.isEmpty(1.0)) {
835 return;
836 }
838 set_display_area(d, 10);
839 }
841 /**
842 * Set display area to current document width.
843 */
844 void
845 SPDesktop::zoom_page_width()
846 {
847 NR::Rect const a = get_display_area();
849 if (sp_document_width(doc()) < 1.0) {
850 return;
851 }
853 NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
854 NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
856 set_display_area(d, 10);
857 }
859 /**
860 * Zoom to selection.
861 */
862 void
863 SPDesktop::zoom_selection()
864 {
865 NR::Maybe<NR::Rect> const d = selection->bounds();
867 if ( !d || d->isEmpty(0.1) ) {
868 return;
869 }
871 set_display_area(*d, 10);
872 }
874 /**
875 * Tell widget to let zoom widget grab keyboard focus.
876 */
877 void
878 SPDesktop::zoom_grab_focus()
879 {
880 _widget->letZoomGrabFocus();
881 }
883 /**
884 * Zoom to whole drawing.
885 */
886 void
887 SPDesktop::zoom_drawing()
888 {
889 g_return_if_fail (doc() != NULL);
890 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
891 g_return_if_fail (docitem != NULL);
893 NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
895 /* Note that the second condition here indicates that
896 ** there are no items in the drawing.
897 */
898 if ( !d || d->isEmpty(1.0) ) {
899 return;
900 }
902 set_display_area(*d, 10);
903 }
905 /**
906 * Scroll canvas by specific coordinate amount.
907 */
908 void
909 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
910 {
911 g_assert(_widget);
913 NR::Rect const viewbox = canvas->getViewbox();
915 sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
917 _widget->updateRulers();
918 _widget->updateScrollbars(expansion(_d2w));
919 }
921 bool
922 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
923 {
924 gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
926 // autoscrolldistance is in screen pixels, but the display area is in document units
927 autoscrolldistance /= expansion(_d2w);
928 NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
930 if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
931 !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y]) ) {
933 NR::Point const s_w( (*p) * _d2w );
935 gdouble x_to;
936 if ((*p)[NR::X] < dbox.min()[NR::X])
937 x_to = dbox.min()[NR::X];
938 else if ((*p)[NR::X] > dbox.max()[NR::X])
939 x_to = dbox.max()[NR::X];
940 else
941 x_to = (*p)[NR::X];
943 gdouble y_to;
944 if ((*p)[NR::Y] < dbox.min()[NR::Y])
945 y_to = dbox.min()[NR::Y];
946 else if ((*p)[NR::Y] > dbox.max()[NR::Y])
947 y_to = dbox.max()[NR::Y];
948 else
949 y_to = (*p)[NR::Y];
951 NR::Point const d_dt(x_to, y_to);
952 NR::Point const d_w( d_dt * _d2w );
953 NR::Point const moved_w( d_w - s_w );
955 if (autoscrollspeed == 0)
956 autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
958 if (autoscrollspeed != 0)
959 scroll_world (autoscrollspeed * moved_w);
961 return true;
962 }
963 return false;
964 }
966 void
967 SPDesktop::fullscreen()
968 {
969 _widget->setFullscreen();
970 }
972 void
973 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
974 {
975 _widget->getGeometry (x, y, w, h);
976 }
978 void
979 SPDesktop::setWindowPosition (NR::Point p)
980 {
981 _widget->setPosition (p);
982 }
984 void
985 SPDesktop::setWindowSize (gint w, gint h)
986 {
987 _widget->setSize (w, h);
988 }
990 void
991 SPDesktop::setWindowTransient (void *p, int transient_policy)
992 {
993 _widget->setTransient (p, transient_policy);
994 }
996 void SPDesktop::getToplevel( GtkWidget*& toplevel )
997 {
998 toplevel = GTK_WIDGET( _widget->getWindow() );
999 }
1001 void
1002 SPDesktop::presentWindow()
1003 {
1004 _widget->present();
1005 }
1007 bool
1008 SPDesktop::warnDialog (gchar *text)
1009 {
1010 return _widget->warnDialog (text);
1011 }
1013 void
1014 SPDesktop::toggleRulers()
1015 {
1016 _widget->toggleRulers();
1017 }
1019 void
1020 SPDesktop::toggleScrollbars()
1021 {
1022 _widget->toggleScrollbars();
1023 }
1025 void
1026 SPDesktop::layoutWidget()
1027 {
1028 _widget->layout();
1029 }
1031 void
1032 SPDesktop::destroyWidget()
1033 {
1034 _widget->destroy();
1035 }
1037 bool
1038 SPDesktop::shutdown()
1039 {
1040 return _widget->shutdown();
1041 }
1043 void
1044 SPDesktop::setToolboxFocusTo (gchar const *label)
1045 {
1046 _widget->setToolboxFocusTo (label);
1047 }
1049 void
1050 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1051 {
1052 _widget->setToolboxAdjustmentValue (id, val);
1053 }
1055 bool
1056 SPDesktop::isToolboxButtonActive (gchar const *id)
1057 {
1058 return _widget->isToolboxButtonActive (id);
1059 }
1061 void
1062 SPDesktop::emitToolSubselectionChanged(gpointer data)
1063 {
1064 _tool_subselection_changed.emit(data);
1065 inkscape_subselection_changed (this);
1066 }
1068 void
1069 SPDesktop::updateNow()
1070 {
1071 sp_canvas_update_now(canvas);
1072 }
1074 void
1075 SPDesktop::enableInteraction()
1076 {
1077 _widget->enableInteraction();
1078 }
1080 void SPDesktop::disableInteraction()
1081 {
1082 _widget->disableInteraction();
1083 }
1085 void SPDesktop::setWaitingCursor()
1086 {
1087 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1088 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1089 gdk_cursor_unref(waiting);
1090 waiting_cursor = true;
1092 // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1093 // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1094 // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1095 // after the call to setWaitingCursor as it was before
1096 while( Gtk::Main::events_pending() )
1097 Gtk::Main::iteration();
1098 }
1100 void SPDesktop::clearWaitingCursor()
1101 {
1102 if (waiting_cursor)
1103 sp_event_context_update_cursor(sp_desktop_event_context(this));
1104 }
1106 void SPDesktop::toggleGrid()
1107 {
1108 if(gridgroup) {
1109 grids_visible = !grids_visible;
1110 if (grids_visible) {
1111 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1112 } else {
1113 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1114 }
1115 }
1116 }
1119 //----------------------------------------------------------------------
1120 // Callback implementations. The virtual ones are connected by the view.
1122 void
1123 SPDesktop::onPositionSet (double x, double y)
1124 {
1125 _widget->viewSetPosition (NR::Point(x,y));
1126 }
1128 void
1129 SPDesktop::onResized (double x, double y)
1130 {
1131 // Nothing called here
1132 }
1134 /**
1135 * Redraw callback; queues Gtk redraw; connected by View.
1136 */
1137 void
1138 SPDesktop::onRedrawRequested ()
1139 {
1140 if (main) {
1141 _widget->requestCanvasUpdate();
1142 }
1143 }
1145 void
1146 SPDesktop::updateCanvasNow()
1147 {
1148 _widget->requestCanvasUpdateAndWait();
1149 }
1151 /**
1152 * Associate document with desktop.
1153 */
1154 /// \todo fixme: refactor SPDesktop::init to use setDocument
1155 void
1156 SPDesktop::setDocument (SPDocument *doc)
1157 {
1158 if (this->doc() && doc) {
1159 namedview->hide(this);
1160 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1161 }
1163 if (_layer_hierarchy) {
1164 _layer_hierarchy->clear();
1165 delete _layer_hierarchy;
1166 }
1167 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1168 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1169 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1170 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1171 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1173 /* setup EventLog */
1174 event_log = new Inkscape::EventLog(doc);
1175 doc->addUndoObserver(*event_log);
1177 _commit_connection.disconnect();
1178 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1180 /// \todo fixme: This condition exists to make sure the code
1181 /// inside is called only once on initialization. But there
1182 /// are surely more safe methods to accomplish this.
1183 if (drawing) {
1184 NRArenaItem *ai;
1186 namedview = sp_document_namedview (doc, NULL);
1187 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1188 number = namedview->getViewCount();
1190 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1191 SP_CANVAS_ARENA (drawing)->arena,
1192 dkey,
1193 SP_ITEM_SHOW_DISPLAY);
1194 if (ai) {
1195 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1196 nr_arena_item_unref (ai);
1197 }
1198 namedview->show(this);
1199 /* Ugly hack */
1200 activate_guides (true);
1201 /* Ugly hack */
1202 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1203 }
1205 _document_replaced_signal.emit (this, doc);
1207 View::setDocument (doc);
1208 }
1210 void
1211 SPDesktop::onStatusMessage
1212 (Inkscape::MessageType type, gchar const *message)
1213 {
1214 if (_widget) {
1215 _widget->setMessage(type, message);
1216 }
1217 }
1219 void
1220 SPDesktop::onDocumentURISet (gchar const* uri)
1221 {
1222 _widget->setTitle(uri);
1223 }
1225 /**
1226 * Resized callback.
1227 */
1228 void
1229 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1230 {
1231 _doc2dt[5] = height;
1232 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1233 NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1234 SP_CTRLRECT(page)->setRectangle(a);
1235 SP_CTRLRECT(page_border)->setRectangle(a);
1236 }
1239 void
1240 SPDesktop::_onActivate (SPDesktop* dt)
1241 {
1242 if (!dt->_widget) return;
1243 dt->_widget->activateDesktop();
1244 }
1246 void
1247 SPDesktop::_onDeactivate (SPDesktop* dt)
1248 {
1249 if (!dt->_widget) return;
1250 dt->_widget->deactivateDesktop();
1251 }
1253 void
1254 SPDesktop::_onSelectionModified
1255 (Inkscape::Selection *selection, guint flags, SPDesktop *dt)
1256 {
1257 if (!dt->_widget) return;
1258 dt->_widget->updateScrollbars (expansion(dt->_d2w));
1259 }
1261 static void
1262 _onSelectionChanged
1263 (Inkscape::Selection *selection, SPDesktop *desktop)
1264 {
1265 /** \todo
1266 * only change the layer for single selections, or what?
1267 * This seems reasonable -- for multiple selections there can be many
1268 * different layers involved.
1269 */
1270 SPItem *item=selection->singleItem();
1271 if (item) {
1272 SPObject *layer=desktop->layerForObject(item);
1273 if ( layer && layer != desktop->currentLayer() ) {
1274 desktop->setCurrentLayer(layer);
1275 }
1276 }
1277 }
1279 /**
1280 * Calls event handler of current event context.
1281 * \param arena Unused
1282 * \todo fixme
1283 */
1284 static gint
1285 _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1286 {
1287 if (ai) {
1288 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1289 return sp_event_context_item_handler (desktop->event_context, spi, event);
1290 } else {
1291 return sp_event_context_root_handler (desktop->event_context, event);
1292 }
1293 }
1295 static void
1296 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1297 g_return_if_fail(SP_IS_GROUP(layer));
1298 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1299 }
1301 /// Callback
1302 static void
1303 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1304 g_return_if_fail(SP_IS_GROUP(layer));
1305 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1306 }
1308 /// Callback
1309 static void
1310 _layer_hierarchy_changed(SPObject *top, SPObject *bottom,
1311 SPDesktop *desktop)
1312 {
1313 desktop->_layer_changed_signal.emit (bottom);
1314 }
1316 /// Called when document is starting to be rebuilt.
1317 static void
1318 _reconstruction_start (SPDesktop * desktop)
1319 {
1320 // printf("Desktop, starting reconstruction\n");
1321 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1322 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1324 /*
1325 GSList const * selection_objs = desktop->selection->list();
1326 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1328 }
1329 */
1330 desktop->selection->clear();
1332 // printf("Desktop, starting reconstruction end\n");
1333 }
1335 /// Called when document rebuild is finished.
1336 static void
1337 _reconstruction_finish (SPDesktop * desktop)
1338 {
1339 // printf("Desktop, finishing reconstruction\n");
1340 if (desktop->_reconstruction_old_layer_id == NULL)
1341 return;
1343 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1344 if (newLayer != NULL)
1345 desktop->setCurrentLayer(newLayer);
1347 g_free(desktop->_reconstruction_old_layer_id);
1348 desktop->_reconstruction_old_layer_id = NULL;
1349 // printf("Desktop, finishing reconstruction end\n");
1350 return;
1351 }
1353 /**
1354 * Namedview_modified callback.
1355 */
1356 static void
1357 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1358 {
1359 SPNamedView *nv=SP_NAMEDVIEW(obj);
1361 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1363 /* Recalculate snap distances */
1364 /* FIXME: why is the desktop getting involved in setting up something
1365 ** that is entirely to do with the namedview?
1366 */
1367 _update_snap_distances (desktop);
1369 /* Show/hide page background */
1370 if (nv->pagecolor & 0xff) {
1371 sp_canvas_item_show (desktop->table);
1372 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1373 sp_canvas_item_move_to_z (desktop->table, 0);
1374 } else {
1375 sp_canvas_item_hide (desktop->table);
1376 }
1378 /* Show/hide page border */
1379 if (nv->showborder) {
1380 // show
1381 sp_canvas_item_show (desktop->page_border);
1382 // set color and shadow
1383 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1384 if (nv->pageshadow) {
1385 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1386 }
1387 // place in the z-order stack
1388 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1389 sp_canvas_item_move_to_z (desktop->page_border, 2);
1390 } else {
1391 int order = sp_canvas_item_order (desktop->page_border);
1392 int morder = sp_canvas_item_order (desktop->drawing);
1393 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1394 morder - order);
1395 }
1396 } else {
1397 sp_canvas_item_hide (desktop->page_border);
1398 if (nv->pageshadow) {
1399 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1400 }
1401 }
1403 /* Show/hide page shadow */
1404 if (nv->showpageshadow && nv->pageshadow) {
1405 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1406 } else {
1407 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1408 }
1410 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1411 (SP_RGBA32_R_U(nv->pagecolor) +
1412 SP_RGBA32_G_U(nv->pagecolor) +
1413 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1414 // the background color is light or transparent, use black outline
1415 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1416 } else { // use white outline
1417 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1418 }
1419 }
1420 }
1422 /**
1423 * Callback to reset snapper's distances.
1424 */
1425 static void
1426 _update_snap_distances (SPDesktop *desktop)
1427 {
1428 SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1430 SPNamedView &nv = *desktop->namedview;
1433 // FIXME GRID: make one gridsnapper object that snaps to all enabled grids by calling their snappers.
1434 nv.snap_manager.grid.setDistance(sp_convert_distance_full(nv.gridtolerance,
1435 *nv.gridtoleranceunit,
1436 px));
1437 //new grid snappers
1438 for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1439 Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1440 grid->snapper->setDistance(sp_convert_distance_full(nv.gridtolerance,
1441 *nv.gridtoleranceunit,
1442 px));
1443 }
1445 nv.snap_manager.guide.setDistance(sp_convert_distance_full(nv.guidetolerance,
1446 *nv.guidetoleranceunit,
1447 px));
1448 nv.snap_manager.object.setDistance(sp_convert_distance_full(nv.objecttolerance,
1449 *nv.objecttoleranceunit,
1450 px));
1451 }
1454 NR::Matrix SPDesktop::w2d() const
1455 {
1456 return _w2d;
1457 }
1459 NR::Point SPDesktop::w2d(NR::Point const &p) const
1460 {
1461 return p * _w2d;
1462 }
1464 NR::Point SPDesktop::d2w(NR::Point const &p) const
1465 {
1466 return p * _d2w;
1467 }
1469 NR::Matrix SPDesktop::doc2dt() const
1470 {
1471 return _doc2dt;
1472 }
1474 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1475 {
1476 return p * _doc2dt;
1477 }
1479 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1480 {
1481 return p / _doc2dt;
1482 }
1485 /**
1486 * Pop event context from desktop's context stack. Never used.
1487 */
1488 // void
1489 // SPDesktop::pop_event_context (unsigned int key)
1490 // {
1491 // SPEventContext *ec = NULL;
1492 //
1493 // if (event_context && event_context->key == key) {
1494 // g_return_if_fail (event_context);
1495 // g_return_if_fail (event_context->next);
1496 // ec = event_context;
1497 // sp_event_context_deactivate (ec);
1498 // event_context = ec->next;
1499 // sp_event_context_activate (event_context);
1500 // _event_context_changed_signal.emit (this, ec);
1501 // }
1502 //
1503 // SPEventContext *ref = event_context;
1504 // while (ref && ref->next && ref->next->key != key)
1505 // ref = ref->next;
1506 //
1507 // if (ref && ref->next) {
1508 // ec = ref->next;
1509 // ref->next = ec->next;
1510 // }
1511 //
1512 // if (ec) {
1513 // sp_event_context_finish (ec);
1514 // g_object_unref (G_OBJECT (ec));
1515 // }
1516 // }
1518 /*
1519 Local Variables:
1520 mode:c++
1521 c-file-style:"stroustrup"
1522 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1523 indent-tabs-mode:nil
1524 fill-column:99
1525 End:
1526 */
1527 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :