Code

Fix missing ruler and document title updates when a file is opened in an existing...
[inkscape.git] / src / desktop.cpp
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) 2007 Jon A. Cruz
15  * Copyright (C) 2006-2007 Johan Engelen
16  * Copyright (C) 2006 John Bintz
17  * Copyright (C) 2004 MenTaLguY
18  * Copyright (C) 1999-2002 Lauris Kaplinski
19  * Copyright (C) 2000-2001 Ximian, Inc.
20  *
21  * Released under GNU GPL, read the file 'COPYING' for more information
22  */
24 /** \class SPDesktop
25  * SPDesktop is a subclass of View, implementing an editable document
26  * canvas.  It is extensively used by many UI controls that need certain
27  * visual representations of their own.
28  *
29  * SPDesktop provides a certain set of SPCanvasItems, serving as GUI
30  * layers of different control objects. The one containing the whole
31  * document is the drawing layer. In addition to it, there are grid,
32  * guide, sketch and control layers. The sketch layer is used for
33  * temporary drawing objects, before the real objects in document are
34  * created. The control layer contains editing knots, rubberband and
35  * similar non-document UI objects.
36  *
37  * Each SPDesktop is associated with a SPNamedView node of the document
38  * tree.  Currently, all desktops are created from a single main named
39  * view, but in the future there may be support for different ones.
40  * SPNamedView serves as an in-document container for desktop-related
41  * data, like grid and guideline placement, snapping options and so on.
42  *
43  * Associated with each SPDesktop are the two most important editing
44  * related objects - SPSelection and SPEventContext.
45  *
46  * Sodipodi keeps track of the active desktop and invokes notification
47  * signals whenever it changes. UI elements can use these to update their
48  * display to the selection of the currently active editing window.
49  * (Lauris Kaplinski)
50  */
52 #ifdef HAVE_CONFIG_H
53 # include "config.h"
54 #endif
56 #include <glibmm/i18n.h>
57 #include <sigc++/functors/mem_fun.h>
58 #include <gtkmm.h>
60 #include "macros.h"
61 #include "inkscape-private.h"
62 #include "desktop.h"
63 #include "desktop-events.h"
64 #include "desktop-handles.h"
65 #include "document.h"
66 #include "message-stack.h"
67 #include "selection.h"
68 #include "select-context.h"
69 #include "sp-namedview.h"
70 #include "color.h"
71 #include "sp-item-group.h"
72 #include "prefs-utils.h"
73 #include "object-hierarchy.h"
74 #include "helper/units.h"
75 #include "display/canvas-arena.h"
76 #include "display/nr-arena.h"
77 #include "display/gnome-canvas-acetate.h"
78 #include "display/sodipodi-ctrlrect.h"
79 #include "display/sp-canvas-util.h"
80 #include "libnr/nr-matrix-div.h"
81 #include "libnr/nr-rect-ops.h"
82 #include "ui/dialog/dialog-manager.h"
83 #include "xml/repr.h"
84 #include "message-context.h"
85 #include "layer-manager.h"
86 #include "event-log.h"
87 #include "display/canvas-grid.h"
88 #include "widgets/desktop-widget.h"
90 #include "display/sp-canvas.h"
92 namespace Inkscape { namespace XML { class Node; }}
94 // Callback declarations
95 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
96 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
97 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
98 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
99 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
100 static void _reconstruction_start(SPDesktop * desktop);
101 static void _reconstruction_finish(SPDesktop * desktop);
102 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
103 static void _update_snap_distances (SPDesktop *desktop);
105 /**
106  * Return new desktop object.
107  * \pre namedview != NULL.
108  * \pre canvas != NULL.
109  */
110 SPDesktop::SPDesktop() :
111     _dlg_mgr( 0 ),
112     namedview( 0 ),
113     canvas( 0 ),
114     selection( 0 ),
115     event_context( 0 ),
116     layer_manager( 0 ),
117     event_log( 0 ),
118     acetate( 0 ),
119     main( 0 ),
120     gridgroup( 0 ),
121     guides( 0 ),
122     drawing( 0 ),
123     sketch( 0 ),
124     controls( 0 ),
125     table( 0 ),
126     page( 0 ),
127     page_border( 0 ),
128     current( 0 ),
129     zooms_past( 0 ),
130     zooms_future( 0 ),
131     dkey( 0 ),
132     number( 0 ),
133     window_state(0),
134     interaction_disabled_counter( 0 ),
135     waiting_cursor( false ),
136     guides_active( false ),
137     gr_item( 0 ),
138     gr_point_type( 0 ),
139     gr_point_i( 0 ),
140     gr_fill_or_stroke( true ),
141     _layer_hierarchy( 0 ),
142     _reconstruction_old_layer_id( 0 ),
143     _widget( 0 ),
144     _inkscape( 0 ),
145     _guides_message_context( 0 ),
146     _active( false ),
147     _w2d(),
148     _d2w(),
149     _doc2dt( NR::Matrix(NR::scale(1, -1)) ),
150     grids_visible( false )
152     _d2w.set_identity();
153     _w2d.set_identity();
155     selection = Inkscape::GC::release( new Inkscape::Selection(this) );
158 void
159 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
161     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
163     current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
165     namedview = nv;
166     canvas = aCanvas;
168     SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
169     /* Kill flicker */
170     sp_document_ensure_up_to_date (document);
172     /* Setup Dialog Manager */
173     _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
175     dkey = sp_item_display_key_new (1);
177     /* Connect document */
178     setDocument (document);
180     number = namedview->getViewCount();
183     /* Setup Canvas */
184     g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
186     SPCanvasGroup *root = sp_canvas_root (canvas);
188     /* Setup adminstrative layers */
189     acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
190     g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
191     main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
192     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
194     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
195     SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
196     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
197     sp_canvas_item_move_to_z (table, 0);
199     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
200     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
201     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
203     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
204     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
206     SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
208     if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
209         // Start in outline mode
210         setDisplayModeOutline();
211     } else {
212         // Start in normal mode, default
213         setDisplayModeNormal();
214     }
216     gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
217     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
218     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
219     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
221     /* Push select tool to the bottom of stack */
222     /** \todo
223      * FIXME: this is the only call to this.  Everything else seems to just
224      * call "set" instead of "push".  Can we assume that there is only one
225      * context ever?
226      */
227     push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
229     // display rect and zoom are now handled in sp_desktop_widget_realize()
231     NR::Rect const d(NR::Point(0.0, 0.0),
232                      NR::Point(sp_document_width(document), sp_document_height(document)));
234     SP_CTRLRECT(page)->setRectangle(d);
235     SP_CTRLRECT(page_border)->setRectangle(d);
237     /* the following sets the page shadow on the canvas
238        It was originally set to 5, which is really cheesy!
239        It now is an attribute in the document's namedview. If a value of
240        0 is used, then the constructor for a shadow is not initialized.
241     */
243     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
244         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
245     }
248     /* Connect event for page resize */
249     _doc2dt[5] = sp_document_height (document);
250     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
252     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
254     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
255             SP_CANVAS_ARENA (drawing)->arena,
256             dkey,
257             SP_ITEM_SHOW_DISPLAY);
258     if (ai) {
259         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
260         nr_arena_item_unref (ai);
261     }
263     namedview->show(this);
264     /* Ugly hack */
265     activate_guides (true);
266     /* Ugly hack */
267     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
269 /* Set up notification of rebuilding the document, this allows
270        for saving object related settings in the document. */
271     _reconstruction_start_connection =
272         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
273     _reconstruction_finish_connection =
274         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
275     _reconstruction_old_layer_id = NULL;
277     // ?
278     // sp_active_desktop_set (desktop);
279     _inkscape = INKSCAPE;
281     _activate_connection = _activate_signal.connect(
282         sigc::bind(
283             sigc::ptr_fun(_onActivate),
284             this
285         )
286     );
287      _deactivate_connection = _deactivate_signal.connect(
288         sigc::bind(
289             sigc::ptr_fun(_onDeactivate),
290             this
291         )
292     );
294     _sel_modified_connection = selection->connectModified(
295         sigc::bind(
296             sigc::ptr_fun(&_onSelectionModified),
297             this
298         )
299     );
300     _sel_changed_connection = selection->connectChanged(
301         sigc::bind(
302             sigc::ptr_fun(&_onSelectionChanged),
303             this
304         )
305     );
308     /* setup LayerManager */
309     //   (Setting up after the connections are all in place, as it may use some of them)
310     layer_manager = new Inkscape::LayerManager( this );
312     showGrids(namedview->grids_visible, false);
316 void SPDesktop::destroy()
318     namedview->hide(this);
320     _activate_connection.disconnect();
321     _deactivate_connection.disconnect();
322     _sel_modified_connection.disconnect();
323     _sel_changed_connection.disconnect();
324     _modified_connection.disconnect();
325     _commit_connection.disconnect();
326     _reconstruction_start_connection.disconnect();
327     _reconstruction_finish_connection.disconnect();
329     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
330     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
331     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
333     while (event_context) {
334         SPEventContext *ec = event_context;
335         event_context = ec->next;
336         sp_event_context_finish (ec);
337         g_object_unref (G_OBJECT (ec));
338     }
340     if (_layer_hierarchy) {
341         delete _layer_hierarchy;
342 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
343     }
345     if (layer_manager) {
346         delete layer_manager;
347         layer_manager = NULL;
348     }
350     if (_inkscape) {
351         _inkscape = NULL;
352     }
354     if (drawing) {
355         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
356         drawing = NULL;
357     }
359     delete _guides_message_context;
360     _guides_message_context = NULL;
362     g_list_free (zooms_past);
363     g_list_free (zooms_future);
366 SPDesktop::~SPDesktop() {}
368 //--------------------------------------------------------------------
369 /* Public methods */
371 void SPDesktop::setDisplayModeNormal()
373     SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
374     canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
375     displayMode = RENDERMODE_NORMAL;
376     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
377     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
380 void SPDesktop::setDisplayModeOutline()
382     SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
383     canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
384     displayMode = RENDERMODE_OUTLINE;
385     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
386     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
389 void SPDesktop::displayModeToggle()
391     if (displayMode == RENDERMODE_OUTLINE)
392         setDisplayModeNormal();
393     else
394         setDisplayModeOutline();
397 /**
398  * Returns current root (=bottom) layer.
399  */
400 SPObject *SPDesktop::currentRoot() const
402     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
405 /**
406  * Returns current top layer.
407  */
408 SPObject *SPDesktop::currentLayer() const
410     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
413 /**
414  * Sets the current layer of the desktop.
415  *
416  * Make \a object the top layer.
417  */
418 void SPDesktop::setCurrentLayer(SPObject *object) {
419     g_return_if_fail(SP_IS_GROUP(object));
420     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
421     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
422     _layer_hierarchy->setBottom(object);
425 /**
426  * Return layer that contains \a object.
427  */
428 SPObject *SPDesktop::layerForObject(SPObject *object) {
429     g_return_val_if_fail(object != NULL, NULL);
431     SPObject *root=currentRoot();
432     object = SP_OBJECT_PARENT(object);
433     while ( object && object != root && !isLayer(object) ) {
434         object = SP_OBJECT_PARENT(object);
435     }
436     return object;
439 /**
440  * True if object is a layer.
441  */
442 bool SPDesktop::isLayer(SPObject *object) const {
443     return ( SP_IS_GROUP(object)
444              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
445                   == SPGroup::LAYER ) );
448 /**
449  * True if desktop viewport fully contains \a item's bbox.
450  */
451 bool SPDesktop::isWithinViewport (SPItem *item) const
453     NR::Rect const viewport = get_display_area();
454     NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
455     if (bbox) {
456         return viewport.contains(*bbox);
457     } else {
458         return true;
459     }
462 ///
463 bool SPDesktop::itemIsHidden(SPItem const *item) const {
464     return item->isHidden(this->dkey);
467 /**
468  * Set activate property of desktop; emit signal if changed.
469  */
470 void
471 SPDesktop::set_active (bool new_active)
473     if (new_active != _active) {
474         _active = new_active;
475         if (new_active) {
476             _activate_signal.emit();
477         } else {
478             _deactivate_signal.emit();
479         }
480     }
483 /**
484  * Set activate status of current desktop's named view.
485  */
486 void
487 SPDesktop::activate_guides(bool activate)
489     guides_active = activate;
490     namedview->activateGuides(this, activate);
493 /**
494  * Make desktop switch documents.
495  */
496 void
497 SPDesktop::change_document (SPDocument *theDocument)
499     g_return_if_fail (theDocument != NULL);
501     /* unselect everything before switching documents */
502     selection->clear();
504     setDocument (theDocument);
506     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
507        (this can probably be done in a better way) */
508     Gtk::Window *parent = this->getToplevel();
509     g_assert(parent != NULL);
510     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
511     if (dtw) dtw->desktop = this;
512     sp_desktop_widget_update_namedview(dtw);
514     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
515     _document_replaced_signal.emit (this, theDocument);
518 /**
519  * Make desktop switch event contexts.
520  */
521 void
522 SPDesktop::set_event_context (GtkType type, const gchar *config)
524     SPEventContext *ec;
525     while (event_context) {
526         ec = event_context;
527         sp_event_context_deactivate (ec);
528         event_context = ec->next;
529         sp_event_context_finish (ec);
530         g_object_unref (G_OBJECT (ec));
531     }
533     Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
534     ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
535     ec->next = event_context;
536     event_context = ec;
537     sp_event_context_activate (ec);
538     _event_context_changed_signal.emit (this, ec);
541 /**
542  * Push event context onto desktop's context stack.
543  */
544 void
545 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
547     SPEventContext *ref, *ec;
548     Inkscape::XML::Node *repr;
550     if (event_context && event_context->key == key) return;
551     ref = event_context;
552     while (ref && ref->next && ref->next->key != key) ref = ref->next;
553     if (ref && ref->next) {
554         ec = ref->next;
555         ref->next = ec->next;
556         sp_event_context_finish (ec);
557         g_object_unref (G_OBJECT (ec));
558     }
560     if (event_context) sp_event_context_deactivate (event_context);
561     repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
562     ec = sp_event_context_new (type, this, repr, key);
563     ec->next = event_context;
564     event_context = ec;
565     sp_event_context_activate (ec);
566     _event_context_changed_signal.emit (this, ec);
569 /**
570  * Sets the coordinate status to a given point
571  */
572 void
573 SPDesktop::set_coordinate_status (NR::Point p) {
574     _widget->setCoordinateStatus(p);
577 /**
578  * \see sp_document_item_from_list_at_point_bottom()
579  */
580 SPItem *
581 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
583     g_return_val_if_fail (doc() != NULL, NULL);
584     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
587 /**
588  * \see sp_document_item_at_point()
589  */
590 SPItem *
591 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
593     g_return_val_if_fail (doc() != NULL, NULL);
594     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
597 /**
598  * \see sp_document_group_at_point()
599  */
600 SPItem *
601 SPDesktop::group_at_point (NR::Point const p) const
603     g_return_val_if_fail (doc() != NULL, NULL);
604     return sp_document_group_at_point (doc(), dkey, p);
607 /**
608  * \brief  Returns the mouse point in document coordinates; if mouse is
609  * outside the canvas, returns the center of canvas viewpoint
610  */
611 NR::Point
612 SPDesktop::point() const
614     NR::Point p = _widget->getPointer();
615     NR::Point pw = sp_canvas_window_to_world (canvas, p);
616     p = w2d(pw);
618     NR::Rect const r = canvas->getViewbox();
620     NR::Point r0 = w2d(r.min());
621     NR::Point r1 = w2d(r.max());
623     if (p[NR::X] >= r0[NR::X] &&
624         p[NR::X] <= r1[NR::X] &&
625         p[NR::Y] >= r1[NR::Y] &&
626         p[NR::Y] <= r0[NR::Y])
627     {
628         return p;
629     } else {
630         return (r0 + r1) / 2;
631     }
634 /**
635  * Put current zoom data in history list.
636  */
637 void
638 SPDesktop::push_current_zoom (GList **history)
640     NR::Rect const area = get_display_area();
642     NRRect *old_zoom = g_new(NRRect, 1);
643     old_zoom->x0 = area.min()[NR::X];
644     old_zoom->x1 = area.max()[NR::X];
645     old_zoom->y0 = area.min()[NR::Y];
646     old_zoom->y1 = area.max()[NR::Y];
647     if ( *history == NULL
648          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
649                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
650                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
651                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
652     {
653         *history = g_list_prepend (*history, old_zoom);
654     }
657 /**
658  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
659  */
660 void
661 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
663     g_assert(_widget);
665     // save the zoom
666     if (log) {
667         push_current_zoom(&zooms_past);
668         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
669         g_list_free (zooms_future);
670         zooms_future = NULL;
671     }
673     double const cx = 0.5 * (x0 + x1);
674     double const cy = 0.5 * (y0 + y1);
676     NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
678     double scale = expansion(_d2w);
679     double newscale;
680     if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
681         newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
682     } else {
683         newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
684     }
686     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
688     int clear = FALSE;
689     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
690         /* Set zoom factors */
691         _d2w = NR::Matrix(NR::scale(newscale, -newscale));
692         _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
693         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
694         clear = TRUE;
695     }
697     /* Calculate top left corner (in document pixels) */
698     x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
699     y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
701     /* Scroll */
702     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
704     _widget->updateRulers();
705     _widget->updateScrollbars(expansion(_d2w));
706     _widget->updateZoom();
709 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
711     set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
714 /**
715  * Return viewbox dimensions.
716  */
717 NR::Rect SPDesktop::get_display_area() const
719     NR::Rect const viewbox = canvas->getViewbox();
721     double const scale = _d2w[0];
723     return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
724                     NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
727 /**
728  * Revert back to previous zoom if possible.
729  */
730 void
731 SPDesktop::prev_zoom()
733     if (zooms_past == NULL) {
734         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
735         return;
736     }
738     // push current zoom into forward zooms list
739     push_current_zoom (&zooms_future);
741     // restore previous zoom
742     set_display_area (((NRRect *) zooms_past->data)->x0,
743             ((NRRect *) zooms_past->data)->y0,
744             ((NRRect *) zooms_past->data)->x1,
745             ((NRRect *) zooms_past->data)->y1,
746             0, false);
748     // remove the just-added zoom from the past zooms list
749     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
752 /**
753  * Set zoom to next in list.
754  */
755 void
756 SPDesktop::next_zoom()
758     if (zooms_future == NULL) {
759         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
760         return;
761     }
763     // push current zoom into past zooms list
764     push_current_zoom (&zooms_past);
766     // restore next zoom
767     set_display_area (((NRRect *) zooms_future->data)->x0,
768             ((NRRect *) zooms_future->data)->y0,
769             ((NRRect *) zooms_future->data)->x1,
770             ((NRRect *) zooms_future->data)->y1,
771             0, false);
773     // remove the just-used zoom from the zooms_future list
774     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
777 /**
778  * Zoom to point with absolute zoom factor.
779  */
780 void
781 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
783     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
785     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
786     // this check prevents "sliding" when trying to zoom in at maximum zoom;
787     /// \todo someone please fix calculations properly and remove this hack
788     if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
789         return;
791     NR::Rect const viewbox = canvas->getViewbox();
793     double const width2 = viewbox.dimensions()[NR::X] / zoom;
794     double const height2 = viewbox.dimensions()[NR::Y] / zoom;
796     set_display_area(cx - px * width2,
797                      cy - py * height2,
798                      cx + (1 - px) * width2,
799                      cy + (1 - py) * height2,
800                      0.0);
803 /**
804  * Zoom to center with absolute zoom factor.
805  */
806 void
807 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
809     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
812 /**
813  * Zoom to point with relative zoom factor.
814  */
815 void
816 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
818     NR::Rect const area = get_display_area();
820     if (cx < area.min()[NR::X]) {
821         cx = area.min()[NR::X];
822     }
823     if (cx > area.max()[NR::X]) {
824         cx = area.max()[NR::X];
825     }
826     if (cy < area.min()[NR::Y]) {
827         cy = area.min()[NR::Y];
828     }
829     if (cy > area.max()[NR::Y]) {
830         cy = area.max()[NR::Y];
831     }
833     gdouble const scale = expansion(_d2w) * zoom;
834     double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
835     double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
837     zoom_absolute_keep_point(cx, cy, px, py, scale);
840 /**
841  * Zoom to center with relative zoom factor.
842  */
843 void
844 SPDesktop::zoom_relative (double cx, double cy, double zoom)
846     gdouble scale = expansion(_d2w) * zoom;
847     zoom_absolute (cx, cy, scale);
850 /**
851  * Set display area to origin and current document dimensions.
852  */
853 void
854 SPDesktop::zoom_page()
856     NR::Rect d(NR::Point(0, 0),
857                NR::Point(sp_document_width(doc()), sp_document_height(doc())));
859     if (d.isEmpty(1.0)) {
860         return;
861     }
863     set_display_area(d, 10);
866 /**
867  * Set display area to current document width.
868  */
869 void
870 SPDesktop::zoom_page_width()
872     NR::Rect const a = get_display_area();
874     if (sp_document_width(doc()) < 1.0) {
875         return;
876     }
878     NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
879                NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
881     set_display_area(d, 10);
884 /**
885  * Zoom to selection.
886  */
887 void
888 SPDesktop::zoom_selection()
890     NR::Maybe<NR::Rect> const d = selection->bounds();
892     if ( !d || d->isEmpty(0.1) ) {
893         return;
894     }
896     set_display_area(*d, 10);
899 /**
900  * Tell widget to let zoom widget grab keyboard focus.
901  */
902 void
903 SPDesktop::zoom_grab_focus()
905     _widget->letZoomGrabFocus();
908 /**
909  * Zoom to whole drawing.
910  */
911 void
912 SPDesktop::zoom_drawing()
914     g_return_if_fail (doc() != NULL);
915     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
916     g_return_if_fail (docitem != NULL);
918     NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
920     /* Note that the second condition here indicates that
921     ** there are no items in the drawing.
922     */
923     if ( !d || d->isEmpty(1.0) ) {
924         return;
925     }
927     set_display_area(*d, 10);
930 /**
931  * Scroll canvas by specific coordinate amount.
932  */
933 void
934 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
936     g_assert(_widget);
938     NR::Rect const viewbox = canvas->getViewbox();
940     sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
942     _widget->updateRulers();
943     _widget->updateScrollbars(expansion(_d2w));
946 bool
947 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
949     gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
951     // autoscrolldistance is in screen pixels, but the display area is in document units
952     autoscrolldistance /= expansion(_d2w);
953     NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
955     if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
956         !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y])   ) {
958         NR::Point const s_w( (*p) * _d2w );
960         gdouble x_to;
961         if ((*p)[NR::X] < dbox.min()[NR::X])
962             x_to = dbox.min()[NR::X];
963         else if ((*p)[NR::X] > dbox.max()[NR::X])
964             x_to = dbox.max()[NR::X];
965         else
966             x_to = (*p)[NR::X];
968         gdouble y_to;
969         if ((*p)[NR::Y] < dbox.min()[NR::Y])
970             y_to = dbox.min()[NR::Y];
971         else if ((*p)[NR::Y] > dbox.max()[NR::Y])
972             y_to = dbox.max()[NR::Y];
973         else
974             y_to = (*p)[NR::Y];
976         NR::Point const d_dt(x_to, y_to);
977         NR::Point const d_w( d_dt * _d2w );
978         NR::Point const moved_w( d_w - s_w );
980         if (autoscrollspeed == 0)
981             autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
983         if (autoscrollspeed != 0)
984             scroll_world (autoscrollspeed * moved_w);
986         return true;
987     }
988     return false;
991 bool
992 SPDesktop::is_iconified()
994     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
997 void
998 SPDesktop::iconify()
1000     _widget->setIconified();
1003 bool
1004 SPDesktop::is_maximized()
1006     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1009 void
1010 SPDesktop::maximize()
1012     _widget->setMaximized();
1015 bool
1016 SPDesktop::is_fullscreen()
1018     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1021 void
1022 SPDesktop::fullscreen()
1024     _widget->setFullscreen();
1027 void
1028 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1030     _widget->getGeometry (x, y, w, h);
1033 void
1034 SPDesktop::setWindowPosition (NR::Point p)
1036     _widget->setPosition (p);
1039 void
1040 SPDesktop::setWindowSize (gint w, gint h)
1042     _widget->setSize (w, h);
1045 void
1046 SPDesktop::setWindowTransient (void *p, int transient_policy)
1048     _widget->setTransient (p, transient_policy);
1051 Gtk::Window*
1052 SPDesktop::getToplevel( )
1054     return _widget->getWindow();
1057 void
1058 SPDesktop::presentWindow()
1060     _widget->present();
1063 bool
1064 SPDesktop::warnDialog (gchar *text)
1066     return _widget->warnDialog (text);
1069 void
1070 SPDesktop::toggleRulers()
1072     _widget->toggleRulers();
1075 void
1076 SPDesktop::toggleScrollbars()
1078     _widget->toggleScrollbars();
1081 void
1082 SPDesktop::layoutWidget()
1084     _widget->layout();
1087 void
1088 SPDesktop::destroyWidget()
1090     _widget->destroy();
1093 bool
1094 SPDesktop::shutdown()
1096     return _widget->shutdown();
1099 bool SPDesktop::onDeleteUI (GdkEventAny*)
1101     if(shutdown()) 
1102         return true;
1104     destroyWidget();
1105     return false;
1108 /**
1109  *  onWindowStateEvent
1110  *
1111  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1112  *  Since GTK doesn't have a way to query this state information directly, we
1113  *  record it for the desktop here, and also possibly trigger a layout.
1114  */
1115 bool
1116 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1118         // Record the desktop window's state
1119     window_state = event->new_window_state;
1121     // Layout may differ depending on full-screen mode or not
1122     GdkWindowState changed = event->changed_mask;
1123     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1124         layoutWidget();
1125     }
1127         return false;
1130 void
1131 SPDesktop::setToolboxFocusTo (gchar const *label)
1133     _widget->setToolboxFocusTo (label);
1136 void
1137 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1139     _widget->setToolboxAdjustmentValue (id, val);
1142 void
1143 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1145     _widget->setToolboxSelectOneValue (id, val);
1148 bool
1149 SPDesktop::isToolboxButtonActive (gchar const *id)
1151     return _widget->isToolboxButtonActive (id);
1154 void
1155 SPDesktop::emitToolSubselectionChanged(gpointer data)
1157         _tool_subselection_changed.emit(data);
1158         inkscape_subselection_changed (this);
1161 void
1162 SPDesktop::updateNow()
1164   sp_canvas_update_now(canvas);
1167 void
1168 SPDesktop::enableInteraction()
1170   _widget->enableInteraction();
1173 void SPDesktop::disableInteraction()
1175   _widget->disableInteraction();
1178 void SPDesktop::setWaitingCursor()
1180     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1181     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1182     gdk_cursor_unref(waiting);
1183     waiting_cursor = true;
1185     // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1186     // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1187     // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1188     // after the call to setWaitingCursor as it was before
1189     while( Gtk::Main::events_pending() )
1190        Gtk::Main::iteration();
1193 void SPDesktop::clearWaitingCursor()
1195   if (waiting_cursor)
1196       sp_event_context_update_cursor(sp_desktop_event_context(this));
1199 void SPDesktop::toggleColorProfAdjust()
1201     _widget->toggleColorProfAdjust();
1204 void SPDesktop::toggleGrids()
1206     if (namedview->grids) {
1207         if(gridgroup) {
1208             showGrids(!grids_visible);
1209         }
1210     } else {
1211         //there is no grid present at the moment. add a rectangular grid and make it visible
1212         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1213         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1214         showGrids(true);
1215     }
1218 void SPDesktop::showGrids(bool show, bool dirty_document)
1220     grids_visible = show;
1221     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1222     if (show) {
1223         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1224     } else {
1225         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1226     }
1229 void SPDesktop::toggleSnapping()
1231     bool v = namedview->snap_manager.getSnapEnabledGlobally();
1232     Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1233     sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1236 //----------------------------------------------------------------------
1237 // Callback implementations. The virtual ones are connected by the view.
1239 void
1240 SPDesktop::onPositionSet (double x, double y)
1242     _widget->viewSetPosition (NR::Point(x,y));
1245 void
1246 SPDesktop::onResized (double /*x*/, double /*y*/)
1248    // Nothing called here
1251 /**
1252  * Redraw callback; queues Gtk redraw; connected by View.
1253  */
1254 void
1255 SPDesktop::onRedrawRequested ()
1257     if (main) {
1258         _widget->requestCanvasUpdate();
1259     }
1262 void
1263 SPDesktop::updateCanvasNow()
1265   _widget->requestCanvasUpdateAndWait();
1268 /**
1269  * Associate document with desktop.
1270  */
1271 void
1272 SPDesktop::setDocument (SPDocument *doc)
1274     if (this->doc() && doc) {
1275         namedview->hide(this);
1276         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1277     }
1279     if (_layer_hierarchy) {
1280         _layer_hierarchy->clear();
1281         delete _layer_hierarchy;
1282     }
1283     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1284     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1285     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1286     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1287     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1289     /* setup EventLog */
1290     event_log = new Inkscape::EventLog(doc);
1291     doc->addUndoObserver(*event_log);
1293     _commit_connection.disconnect();
1294     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1296     /// \todo fixme: This condition exists to make sure the code
1297     /// inside is NOT called on initialization, only on replacement. But there
1298     /// are surely more safe methods to accomplish this.
1299     // TODO since the comment had reversed logic, check the intent of this block of code:
1300     if (drawing) {
1301         NRArenaItem *ai = 0;
1303         namedview = sp_document_namedview (doc, NULL);
1304         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1305         number = namedview->getViewCount();
1307         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1308                 SP_CANVAS_ARENA (drawing)->arena,
1309                 dkey,
1310                 SP_ITEM_SHOW_DISPLAY);
1311         if (ai) {
1312             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1313             nr_arena_item_unref (ai);
1314         }
1315         namedview->show(this);
1316         /* Ugly hack */
1317         activate_guides (true);
1318         /* Ugly hack */
1319         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1320     }
1322     _document_replaced_signal.emit (this, doc);
1324     View::setDocument (doc);
1327 void
1328 SPDesktop::onStatusMessage
1329 (Inkscape::MessageType type, gchar const *message)
1331     if (_widget) {
1332         _widget->setMessage(type, message);
1333     }
1336 void
1337 SPDesktop::onDocumentURISet (gchar const* uri)
1339     _widget->setTitle(uri);
1342 /**
1343  * Resized callback.
1344  */
1345 void
1346 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1348     _doc2dt[5] = height;
1349     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1350     NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1351     SP_CTRLRECT(page)->setRectangle(a);
1352     SP_CTRLRECT(page_border)->setRectangle(a);
1356 void
1357 SPDesktop::_onActivate (SPDesktop* dt)
1359     if (!dt->_widget) return;
1360     dt->_widget->activateDesktop();
1363 void
1364 SPDesktop::_onDeactivate (SPDesktop* dt)
1366     if (!dt->_widget) return;
1367     dt->_widget->deactivateDesktop();
1370 void
1371 SPDesktop::_onSelectionModified
1372 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1374     if (!dt->_widget) return;
1375     dt->_widget->updateScrollbars (expansion(dt->_d2w));
1378 static void
1379 _onSelectionChanged
1380 (Inkscape::Selection *selection, SPDesktop *desktop)
1382     /** \todo
1383      * only change the layer for single selections, or what?
1384      * This seems reasonable -- for multiple selections there can be many
1385      * different layers involved.
1386      */
1387     SPItem *item=selection->singleItem();
1388     if (item) {
1389         SPObject *layer=desktop->layerForObject(item);
1390         if ( layer && layer != desktop->currentLayer() ) {
1391             desktop->setCurrentLayer(layer);
1392         }
1393     }
1396 /**
1397  * Calls event handler of current event context.
1398  * \param arena Unused
1399  * \todo fixme
1400  */
1401 static gint
1402 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1404     if (ai) {
1405         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1406         return sp_event_context_item_handler (desktop->event_context, spi, event);
1407     } else {
1408         return sp_event_context_root_handler (desktop->event_context, event);
1409     }
1412 static void
1413 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1414     g_return_if_fail(SP_IS_GROUP(layer));
1415     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1418 /// Callback
1419 static void
1420 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1421     g_return_if_fail(SP_IS_GROUP(layer));
1422     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1425 /// Callback
1426 static void
1427 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1428                                          SPDesktop *desktop)
1430     desktop->_layer_changed_signal.emit (bottom);
1433 /// Called when document is starting to be rebuilt.
1434 static void
1435 _reconstruction_start (SPDesktop * desktop)
1437     // printf("Desktop, starting reconstruction\n");
1438     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1439     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1441     /*
1442     GSList const * selection_objs = desktop->selection->list();
1443     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1445     }
1446     */
1447     desktop->selection->clear();
1449     // printf("Desktop, starting reconstruction end\n");
1452 /// Called when document rebuild is finished.
1453 static void
1454 _reconstruction_finish (SPDesktop * desktop)
1456     // printf("Desktop, finishing reconstruction\n");
1457     if (desktop->_reconstruction_old_layer_id == NULL)
1458         return;
1460     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1461     if (newLayer != NULL)
1462         desktop->setCurrentLayer(newLayer);
1464     g_free(desktop->_reconstruction_old_layer_id);
1465     desktop->_reconstruction_old_layer_id = NULL;
1466     // printf("Desktop, finishing reconstruction end\n");
1467     return;
1470 /**
1471  * Namedview_modified callback.
1472  */
1473 static void
1474 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1476     SPNamedView *nv=SP_NAMEDVIEW(obj);
1478     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1480         /* Recalculate snap distances */
1481         /* FIXME: why is the desktop getting involved in setting up something
1482         ** that is entirely to do with the namedview?
1483         */
1484         _update_snap_distances (desktop);
1486         /* Show/hide page background */
1487         if (nv->pagecolor & 0xff) {
1488             sp_canvas_item_show (desktop->table);
1489             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1490             sp_canvas_item_move_to_z (desktop->table, 0);
1491         } else {
1492             sp_canvas_item_hide (desktop->table);
1493         }
1495         /* Show/hide page border */
1496         if (nv->showborder) {
1497             // show
1498             sp_canvas_item_show (desktop->page_border);
1499             // set color and shadow
1500             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1501             if (nv->pageshadow) {
1502                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1503             }
1504             // place in the z-order stack
1505             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1506                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1507             } else {
1508                 int order = sp_canvas_item_order (desktop->page_border);
1509                 int morder = sp_canvas_item_order (desktop->drawing);
1510                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1511                                     morder - order);
1512             }
1513         } else {
1514                 sp_canvas_item_hide (desktop->page_border);
1515                 if (nv->pageshadow) {
1516                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1517                 }
1518         }
1520         /* Show/hide page shadow */
1521         if (nv->showpageshadow && nv->pageshadow) {
1522             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1523         } else {
1524             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1525         }
1527         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1528             (SP_RGBA32_R_U(nv->pagecolor) +
1529              SP_RGBA32_G_U(nv->pagecolor) +
1530              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1531             // the background color is light or transparent, use black outline
1532             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1533         } else { // use white outline
1534             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1535         }
1536     }
1539 /**
1540  * Callback to reset snapper's distances.
1541  */
1542 static void
1543 _update_snap_distances (SPDesktop *desktop)
1545     SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1547     SPNamedView &nv = *desktop->namedview;
1549     //tell all grid snappers
1550     for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1551         Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1552         grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
1553                                                                       *nv.gridtoleranceunit,
1554                                                                       px));
1555     }
1557     nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
1558                                                                        *nv.guidetoleranceunit,
1559                                                                        px));
1560     nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
1561                                                                         *nv.objecttoleranceunit,
1562                                                                         px));
1566 NR::Matrix SPDesktop::w2d() const
1568     return _w2d;
1571 NR::Point SPDesktop::w2d(NR::Point const &p) const
1573     return p * _w2d;
1576 NR::Point SPDesktop::d2w(NR::Point const &p) const
1578     return p * _d2w;
1581 NR::Matrix SPDesktop::doc2dt() const
1583     return _doc2dt;
1586 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1588     return p * _doc2dt;
1591 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1593     return p / _doc2dt;
1597 /**
1598  * Pop event context from desktop's context stack. Never used.
1599  */
1600 // void
1601 // SPDesktop::pop_event_context (unsigned int key)
1602 // {
1603 //    SPEventContext *ec = NULL;
1604 //
1605 //    if (event_context && event_context->key == key) {
1606 //        g_return_if_fail (event_context);
1607 //        g_return_if_fail (event_context->next);
1608 //        ec = event_context;
1609 //        sp_event_context_deactivate (ec);
1610 //        event_context = ec->next;
1611 //        sp_event_context_activate (event_context);
1612 //        _event_context_changed_signal.emit (this, ec);
1613 //    }
1614 //
1615 //    SPEventContext *ref = event_context;
1616 //    while (ref && ref->next && ref->next->key != key)
1617 //        ref = ref->next;
1618 //
1619 //    if (ref && ref->next) {
1620 //        ec = ref->next;
1621 //        ref->next = ec->next;
1622 //    }
1623 //
1624 //    if (ec) {
1625 //        sp_event_context_finish (ec);
1626 //        g_object_unref (G_OBJECT (ec));
1627 //    }
1628 // }
1630 /*
1631   Local Variables:
1632   mode:c++
1633   c-file-style:"stroustrup"
1634   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1635   indent-tabs-mode:nil
1636   fill-column:99
1637   End:
1638 */
1639 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :