Code

Don't set inkscape:modified on startup when grids are shown/hidden.
[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"
89 #include "display/sp-canvas.h"
91 namespace Inkscape { namespace XML { class Node; }}
93 // Callback declarations
94 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
95 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
96 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
97 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
98 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
99 static void _reconstruction_start(SPDesktop * desktop);
100 static void _reconstruction_finish(SPDesktop * desktop);
101 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
102 static void _update_snap_distances (SPDesktop *desktop);
104 /**
105  * Return new desktop object.
106  * \pre namedview != NULL.
107  * \pre canvas != NULL.
108  */
109 SPDesktop::SPDesktop() :
110     _dlg_mgr( 0 ),
111     namedview( 0 ),
112     canvas( 0 ),
113     selection( 0 ),
114     event_context( 0 ),
115     layer_manager( 0 ),
116     event_log( 0 ),
117     acetate( 0 ),
118     main( 0 ),
119     gridgroup( 0 ),
120     guides( 0 ),
121     drawing( 0 ),
122     sketch( 0 ),
123     controls( 0 ),
124     table( 0 ),
125     page( 0 ),
126     page_border( 0 ),
127     current( 0 ),
128     zooms_past( 0 ),
129     zooms_future( 0 ),
130     dkey( 0 ),
131     number( 0 ),
132     window_state(0),
133     interaction_disabled_counter( 0 ),
134     waiting_cursor( false ),
135     guides_active( false ),
136     gr_item( 0 ),
137     gr_point_type( 0 ),
138     gr_point_i( 0 ),
139     gr_fill_or_stroke( true ),
140     _layer_hierarchy( 0 ),
141     _reconstruction_old_layer_id( 0 ),
142     _widget( 0 ),
143     _inkscape( 0 ),
144     _guides_message_context( 0 ),
145     _active( false ),
146     _w2d(),
147     _d2w(),
148     _doc2dt( NR::Matrix(NR::scale(1, -1)) ),
149     grids_visible( false )
151     _d2w.set_identity();
152     _w2d.set_identity();
154     selection = Inkscape::GC::release( new Inkscape::Selection(this) );
157 void
158 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
160     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
162     current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
164     namedview = nv;
165     canvas = aCanvas;
167     SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
168     /* Kill flicker */
169     sp_document_ensure_up_to_date (document);
171     /* Setup Dialog Manager */
172     _dlg_mgr = new Inkscape::UI::Dialog::DialogManager();
174     dkey = sp_item_display_key_new (1);
176     /* Connect document */
177     setDocument (document);
179     number = namedview->getViewCount();
182     /* Setup Canvas */
183     g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
185     SPCanvasGroup *root = sp_canvas_root (canvas);
187     /* Setup adminstrative layers */
188     acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
189     g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
190     main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
191     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
193     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
194     SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
195     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
196     sp_canvas_item_move_to_z (table, 0);
198     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
199     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
200     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
202     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
203     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
205     SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
207     if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
208         // Start in outline mode
209         setDisplayModeOutline();
210     } else {
211         // Start in normal mode, default
212         setDisplayModeNormal();
213     }
215     gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
216     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
217     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
218     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
220     /* Push select tool to the bottom of stack */
221     /** \todo
222      * FIXME: this is the only call to this.  Everything else seems to just
223      * call "set" instead of "push".  Can we assume that there is only one
224      * context ever?
225      */
226     push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
228     // display rect and zoom are now handled in sp_desktop_widget_realize()
230     NR::Rect const d(NR::Point(0.0, 0.0),
231                      NR::Point(sp_document_width(document), sp_document_height(document)));
233     SP_CTRLRECT(page)->setRectangle(d);
234     SP_CTRLRECT(page_border)->setRectangle(d);
236     /* the following sets the page shadow on the canvas
237        It was originally set to 5, which is really cheesy!
238        It now is an attribute in the document's namedview. If a value of
239        0 is used, then the constructor for a shadow is not initialized.
240     */
242     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
243         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
244     }
247     /* Connect event for page resize */
248     _doc2dt[5] = sp_document_height (document);
249     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
251     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
253     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
254             SP_CANVAS_ARENA (drawing)->arena,
255             dkey,
256             SP_ITEM_SHOW_DISPLAY);
257     if (ai) {
258         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
259         nr_arena_item_unref (ai);
260     }
262     namedview->show(this);
263     /* Ugly hack */
264     activate_guides (true);
265     /* Ugly hack */
266     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
268 /* Set up notification of rebuilding the document, this allows
269        for saving object related settings in the document. */
270     _reconstruction_start_connection =
271         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
272     _reconstruction_finish_connection =
273         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
274     _reconstruction_old_layer_id = NULL;
276     // ?
277     // sp_active_desktop_set (desktop);
278     _inkscape = INKSCAPE;
280     _activate_connection = _activate_signal.connect(
281         sigc::bind(
282             sigc::ptr_fun(_onActivate),
283             this
284         )
285     );
286      _deactivate_connection = _deactivate_signal.connect(
287         sigc::bind(
288             sigc::ptr_fun(_onDeactivate),
289             this
290         )
291     );
293     _sel_modified_connection = selection->connectModified(
294         sigc::bind(
295             sigc::ptr_fun(&_onSelectionModified),
296             this
297         )
298     );
299     _sel_changed_connection = selection->connectChanged(
300         sigc::bind(
301             sigc::ptr_fun(&_onSelectionChanged),
302             this
303         )
304     );
307     /* setup LayerManager */
308     //   (Setting up after the connections are all in place, as it may use some of them)
309     layer_manager = new Inkscape::LayerManager( this );
311     showGrids(namedview->grids_visible, false);
315 void SPDesktop::destroy()
317     _activate_connection.disconnect();
318     _deactivate_connection.disconnect();
319     _sel_modified_connection.disconnect();
320     _sel_changed_connection.disconnect();
321     _modified_connection.disconnect();
322     _commit_connection.disconnect();
323     _reconstruction_start_connection.disconnect();
324     _reconstruction_finish_connection.disconnect();
326     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
327     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
328     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
330     while (event_context) {
331         SPEventContext *ec = event_context;
332         event_context = ec->next;
333         sp_event_context_finish (ec);
334         g_object_unref (G_OBJECT (ec));
335     }
337     if (_layer_hierarchy) {
338         delete _layer_hierarchy;
339 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
340     }
342     if (layer_manager) {
343         delete layer_manager;
344         layer_manager = NULL;
345     }
347     if (_inkscape) {
348         _inkscape = NULL;
349     }
351     if (drawing) {
352         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
353         drawing = NULL;
354     }
356     delete _guides_message_context;
357     _guides_message_context = NULL;
359     g_list_free (zooms_past);
360     g_list_free (zooms_future);
363 SPDesktop::~SPDesktop() {}
365 //--------------------------------------------------------------------
366 /* Public methods */
368 void SPDesktop::setDisplayModeNormal()
370     SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
371     canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
372     displayMode = RENDERMODE_NORMAL;
373     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
374     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
377 void SPDesktop::setDisplayModeOutline()
379     SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
380     canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
381     displayMode = RENDERMODE_OUTLINE;
382     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
383     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
386 void SPDesktop::displayModeToggle()
388     if (displayMode == RENDERMODE_OUTLINE)
389         setDisplayModeNormal();
390     else 
391         setDisplayModeOutline();
394 /**
395  * Returns current root (=bottom) layer.
396  */
397 SPObject *SPDesktop::currentRoot() const
399     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
402 /**
403  * Returns current top layer.
404  */
405 SPObject *SPDesktop::currentLayer() const
407     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
410 /**
411  * Sets the current layer of the desktop.
412  * 
413  * Make \a object the top layer.
414  */
415 void SPDesktop::setCurrentLayer(SPObject *object) {
416     g_return_if_fail(SP_IS_GROUP(object));
417     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
418     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
419     _layer_hierarchy->setBottom(object);
422 /**
423  * Return layer that contains \a object.
424  */
425 SPObject *SPDesktop::layerForObject(SPObject *object) {
426     g_return_val_if_fail(object != NULL, NULL);
428     SPObject *root=currentRoot();
429     object = SP_OBJECT_PARENT(object);
430     while ( object && object != root && !isLayer(object) ) {
431         object = SP_OBJECT_PARENT(object);
432     }
433     return object;
436 /**
437  * True if object is a layer.
438  */
439 bool SPDesktop::isLayer(SPObject *object) const {
440     return ( SP_IS_GROUP(object)
441              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
442                   == SPGroup::LAYER ) );
445 /**
446  * True if desktop viewport fully contains \a item's bbox.
447  */
448 bool SPDesktop::isWithinViewport (SPItem *item) const
450     NR::Rect const viewport = get_display_area();
451     NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
452     if (bbox) {
453         return viewport.contains(*bbox);
454     } else {
455         return true;
456     }
459 ///
460 bool SPDesktop::itemIsHidden(SPItem const *item) const {
461     return item->isHidden(this->dkey);
464 /**
465  * Set activate property of desktop; emit signal if changed.
466  */
467 void
468 SPDesktop::set_active (bool new_active)
470     if (new_active != _active) {
471         _active = new_active;
472         if (new_active) {
473             _activate_signal.emit();
474         } else {
475             _deactivate_signal.emit();
476         }
477     }
480 /**
481  * Set activate status of current desktop's named view.
482  */
483 void
484 SPDesktop::activate_guides(bool activate)
486     guides_active = activate;
487     namedview->activateGuides(this, activate);
490 /**
491  * Make desktop switch documents.
492  */
493 void
494 SPDesktop::change_document (SPDocument *theDocument)
496     g_return_if_fail (theDocument != NULL);
498     /* unselect everything before switching documents */
499     selection->clear();
501     setDocument (theDocument);
502     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
503     _document_replaced_signal.emit (this, theDocument);
506 /**
507  * Make desktop switch event contexts.
508  */
509 void
510 SPDesktop::set_event_context (GtkType type, const gchar *config)
512     SPEventContext *ec;
513     while (event_context) {
514         ec = event_context;
515         sp_event_context_deactivate (ec);
516         event_context = ec->next;
517         sp_event_context_finish (ec);
518         g_object_unref (G_OBJECT (ec));
519     }
521     Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
522     ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
523     ec->next = event_context;
524     event_context = ec;
525     sp_event_context_activate (ec);
526     _event_context_changed_signal.emit (this, ec);
529 /**
530  * Push event context onto desktop's context stack.
531  */
532 void
533 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
535     SPEventContext *ref, *ec;
536     Inkscape::XML::Node *repr;
538     if (event_context && event_context->key == key) return;
539     ref = event_context;
540     while (ref && ref->next && ref->next->key != key) ref = ref->next;
541     if (ref && ref->next) {
542         ec = ref->next;
543         ref->next = ec->next;
544         sp_event_context_finish (ec);
545         g_object_unref (G_OBJECT (ec));
546     }
548     if (event_context) sp_event_context_deactivate (event_context);
549     repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
550     ec = sp_event_context_new (type, this, repr, key);
551     ec->next = event_context;
552     event_context = ec;
553     sp_event_context_activate (ec);
554     _event_context_changed_signal.emit (this, ec);
557 /**
558  * Sets the coordinate status to a given point
559  */
560 void
561 SPDesktop::set_coordinate_status (NR::Point p) {
562     _widget->setCoordinateStatus(p);
565 /**
566  * \see sp_document_item_from_list_at_point_bottom()
567  */
568 SPItem *
569 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
571     g_return_val_if_fail (doc() != NULL, NULL);
572     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
575 /**
576  * \see sp_document_item_at_point()
577  */
578 SPItem *
579 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
581     g_return_val_if_fail (doc() != NULL, NULL);
582     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
585 /**
586  * \see sp_document_group_at_point()
587  */
588 SPItem *
589 SPDesktop::group_at_point (NR::Point const p) const
591     g_return_val_if_fail (doc() != NULL, NULL);
592     return sp_document_group_at_point (doc(), dkey, p);
595 /**
596  * \brief  Returns the mouse point in document coordinates; if mouse is
597  * outside the canvas, returns the center of canvas viewpoint
598  */
599 NR::Point
600 SPDesktop::point() const
602     NR::Point p = _widget->getPointer();
603     NR::Point pw = sp_canvas_window_to_world (canvas, p);
604     p = w2d(pw);
606     NR::Rect const r = canvas->getViewbox();
608     NR::Point r0 = w2d(r.min());
609     NR::Point r1 = w2d(r.max());
611     if (p[NR::X] >= r0[NR::X] &&
612         p[NR::X] <= r1[NR::X] &&
613         p[NR::Y] >= r1[NR::Y] &&
614         p[NR::Y] <= r0[NR::Y])
615     {
616         return p;
617     } else {
618         return (r0 + r1) / 2;
619     }
622 /**
623  * Put current zoom data in history list.
624  */
625 void
626 SPDesktop::push_current_zoom (GList **history)
628     NR::Rect const area = get_display_area();
630     NRRect *old_zoom = g_new(NRRect, 1);
631     old_zoom->x0 = area.min()[NR::X];
632     old_zoom->x1 = area.max()[NR::X];
633     old_zoom->y0 = area.min()[NR::Y];
634     old_zoom->y1 = area.max()[NR::Y];
635     if ( *history == NULL
636          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
637                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
638                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
639                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
640     {
641         *history = g_list_prepend (*history, old_zoom);
642     }
645 /**
646  * Set viewbox.
647  */
648 void
649 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
651     g_assert(_widget);
653     // save the zoom
654     if (log) {
655         push_current_zoom(&zooms_past);
656         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
657         g_list_free (zooms_future);
658         zooms_future = NULL;
659     }
661     double const cx = 0.5 * (x0 + x1);
662     double const cy = 0.5 * (y0 + y1);
664     NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
666     double scale = expansion(_d2w);
667     double newscale;
668     if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
669         newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
670     } else {
671         newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
672     }
674     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
676     int clear = FALSE;
677     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
678         /* Set zoom factors */
679         _d2w = NR::Matrix(NR::scale(newscale, -newscale));
680         _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
681         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
682         clear = TRUE;
683     }
685     /* Calculate top left corner */
686     x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
687     y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
689     /* Scroll */
690     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
692     _widget->updateRulers();
693     _widget->updateScrollbars(expansion(_d2w));
694     _widget->updateZoom();
697 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
699     set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
702 /**
703  * Return viewbox dimensions.
704  */
705 NR::Rect SPDesktop::get_display_area() const
707     NR::Rect const viewbox = canvas->getViewbox();
709     double const scale = _d2w[0];
711     return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
712                     NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
715 /**
716  * Revert back to previous zoom if possible.
717  */
718 void
719 SPDesktop::prev_zoom()
721     if (zooms_past == NULL) {
722         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
723         return;
724     }
726     // push current zoom into forward zooms list
727     push_current_zoom (&zooms_future);
729     // restore previous zoom
730     set_display_area (((NRRect *) zooms_past->data)->x0,
731             ((NRRect *) zooms_past->data)->y0,
732             ((NRRect *) zooms_past->data)->x1,
733             ((NRRect *) zooms_past->data)->y1,
734             0, false);
736     // remove the just-added zoom from the past zooms list
737     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
740 /**
741  * Set zoom to next in list.
742  */
743 void
744 SPDesktop::next_zoom()
746     if (zooms_future == NULL) {
747         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
748         return;
749     }
751     // push current zoom into past zooms list
752     push_current_zoom (&zooms_past);
754     // restore next zoom
755     set_display_area (((NRRect *) zooms_future->data)->x0,
756             ((NRRect *) zooms_future->data)->y0,
757             ((NRRect *) zooms_future->data)->x1,
758             ((NRRect *) zooms_future->data)->y1,
759             0, false);
761     // remove the just-used zoom from the zooms_future list
762     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
765 /**
766  * Zoom to point with absolute zoom factor.
767  */
768 void
769 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
771     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
773     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
774     // this check prevents "sliding" when trying to zoom in at maximum zoom;
775     /// \todo someone please fix calculations properly and remove this hack
776     if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
777         return;
779     NR::Rect const viewbox = canvas->getViewbox();
781     double const width2 = viewbox.dimensions()[NR::X] / zoom;
782     double const height2 = viewbox.dimensions()[NR::Y] / zoom;
784     set_display_area(cx - px * width2,
785                      cy - py * height2,
786                      cx + (1 - px) * width2,
787                      cy + (1 - py) * height2,
788                      0.0);
791 /**
792  * Zoom to center with absolute zoom factor.
793  */
794 void
795 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
797     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
800 /**
801  * Zoom to point with relative zoom factor.
802  */
803 void
804 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
806     NR::Rect const area = get_display_area();
808     if (cx < area.min()[NR::X]) {
809         cx = area.min()[NR::X];
810     }
811     if (cx > area.max()[NR::X]) {
812         cx = area.max()[NR::X];
813     }
814     if (cy < area.min()[NR::Y]) {
815         cy = area.min()[NR::Y];
816     }
817     if (cy > area.max()[NR::Y]) {
818         cy = area.max()[NR::Y];
819     }
821     gdouble const scale = expansion(_d2w) * zoom;
822     double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
823     double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
825     zoom_absolute_keep_point(cx, cy, px, py, scale);
828 /**
829  * Zoom to center with relative zoom factor.
830  */
831 void
832 SPDesktop::zoom_relative (double cx, double cy, double zoom)
834     gdouble scale = expansion(_d2w) * zoom;
835     zoom_absolute (cx, cy, scale);
838 /**
839  * Set display area to origin and current document dimensions.
840  */
841 void
842 SPDesktop::zoom_page()
844     NR::Rect d(NR::Point(0, 0),
845                NR::Point(sp_document_width(doc()), sp_document_height(doc())));
847     if (d.isEmpty(1.0)) {
848         return;
849     }
851     set_display_area(d, 10);
854 /**
855  * Set display area to current document width.
856  */
857 void
858 SPDesktop::zoom_page_width()
860     NR::Rect const a = get_display_area();
862     if (sp_document_width(doc()) < 1.0) {
863         return;
864     }
866     NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
867                NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
869     set_display_area(d, 10);
872 /**
873  * Zoom to selection.
874  */
875 void
876 SPDesktop::zoom_selection()
878     NR::Maybe<NR::Rect> const d = selection->bounds();
880     if ( !d || d->isEmpty(0.1) ) {
881         return;
882     }
884     set_display_area(*d, 10);
887 /**
888  * Tell widget to let zoom widget grab keyboard focus.
889  */
890 void
891 SPDesktop::zoom_grab_focus()
893     _widget->letZoomGrabFocus();
896 /**
897  * Zoom to whole drawing.
898  */
899 void
900 SPDesktop::zoom_drawing()
902     g_return_if_fail (doc() != NULL);
903     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
904     g_return_if_fail (docitem != NULL);
906     NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
908     /* Note that the second condition here indicates that
909     ** there are no items in the drawing.
910     */
911     if ( !d || d->isEmpty(1.0) ) {
912         return;
913     }
915     set_display_area(*d, 10);
918 /**
919  * Scroll canvas by specific coordinate amount.
920  */
921 void
922 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
924     g_assert(_widget);
926     NR::Rect const viewbox = canvas->getViewbox();
928     sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
930     _widget->updateRulers();
931     _widget->updateScrollbars(expansion(_d2w));
934 bool
935 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
937     gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
939     // autoscrolldistance is in screen pixels, but the display area is in document units
940     autoscrolldistance /= expansion(_d2w);
941     NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
943     if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
944         !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y])   ) {
946         NR::Point const s_w( (*p) * _d2w );
948         gdouble x_to;
949         if ((*p)[NR::X] < dbox.min()[NR::X])
950             x_to = dbox.min()[NR::X];
951         else if ((*p)[NR::X] > dbox.max()[NR::X])
952             x_to = dbox.max()[NR::X];
953         else
954             x_to = (*p)[NR::X];
956         gdouble y_to;
957         if ((*p)[NR::Y] < dbox.min()[NR::Y])
958             y_to = dbox.min()[NR::Y];
959         else if ((*p)[NR::Y] > dbox.max()[NR::Y])
960             y_to = dbox.max()[NR::Y];
961         else
962             y_to = (*p)[NR::Y];
964         NR::Point const d_dt(x_to, y_to);
965         NR::Point const d_w( d_dt * _d2w );
966         NR::Point const moved_w( d_w - s_w );
968         if (autoscrollspeed == 0)
969             autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
971         if (autoscrollspeed != 0)
972             scroll_world (autoscrollspeed * moved_w);
974         return true;
975     }
976     return false;
979 bool
980 SPDesktop::is_iconified()
982     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
985 void
986 SPDesktop::iconify()
988     _widget->setIconified();
991 bool
992 SPDesktop::is_maximized()
994     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
997 void
998 SPDesktop::maximize()
1000     _widget->setMaximized();
1003 bool
1004 SPDesktop::is_fullscreen()
1006     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1009 void
1010 SPDesktop::fullscreen()
1012     _widget->setFullscreen();
1015 void
1016 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1018     _widget->getGeometry (x, y, w, h);
1021 void
1022 SPDesktop::setWindowPosition (NR::Point p)
1024     _widget->setPosition (p);
1027 void
1028 SPDesktop::setWindowSize (gint w, gint h)
1030     _widget->setSize (w, h);
1033 void
1034 SPDesktop::setWindowTransient (void *p, int transient_policy)
1036     _widget->setTransient (p, transient_policy);
1039 Gtk::Window*
1040 SPDesktop::getToplevel( )
1042     return _widget->getWindow();
1045 void
1046 SPDesktop::presentWindow()
1048     _widget->present();
1051 bool
1052 SPDesktop::warnDialog (gchar *text)
1054     return _widget->warnDialog (text);
1057 void
1058 SPDesktop::toggleRulers()
1060     _widget->toggleRulers();
1063 void
1064 SPDesktop::toggleScrollbars()
1066     _widget->toggleScrollbars();
1069 void
1070 SPDesktop::layoutWidget()
1072     _widget->layout();
1075 void
1076 SPDesktop::destroyWidget()
1078     _widget->destroy();
1081 bool
1082 SPDesktop::shutdown()
1084     return _widget->shutdown();
1087 bool SPDesktop::onDeleteUI (GdkEventAny*)
1089         if(shutdown()) return true;
1090         destroyWidget();
1091         return false;
1094 /**
1095  *  onWindowStateEvent
1096  *
1097  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1098  *  Since GTK doesn't have a way to query this state information directly, we
1099  *  record it for the desktop here, and also possibly trigger a layout.
1100  */
1101 bool
1102 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1104         // Record the desktop window's state
1105     window_state = event->new_window_state;
1107     // Layout may differ depending on full-screen mode or not
1108     GdkWindowState changed = event->changed_mask;
1109     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1110         layoutWidget();
1111     }
1112         
1113         return false;
1116 void
1117 SPDesktop::setToolboxFocusTo (gchar const *label)
1119     _widget->setToolboxFocusTo (label);
1122 void
1123 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1125     _widget->setToolboxAdjustmentValue (id, val);
1128 void
1129 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1131     _widget->setToolboxSelectOneValue (id, val);
1134 bool
1135 SPDesktop::isToolboxButtonActive (gchar const *id)
1137     return _widget->isToolboxButtonActive (id);
1140 void
1141 SPDesktop::emitToolSubselectionChanged(gpointer data)
1143         _tool_subselection_changed.emit(data);
1144         inkscape_subselection_changed (this);
1147 void
1148 SPDesktop::updateNow()
1150   sp_canvas_update_now(canvas);
1153 void
1154 SPDesktop::enableInteraction()
1156   _widget->enableInteraction();
1159 void SPDesktop::disableInteraction()
1161   _widget->disableInteraction();
1164 void SPDesktop::setWaitingCursor()
1166     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1167     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1168     gdk_cursor_unref(waiting);
1169     waiting_cursor = true;
1171     // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1172     // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1173     // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1174     // after the call to setWaitingCursor as it was before
1175     while( Gtk::Main::events_pending() )
1176        Gtk::Main::iteration();
1179 void SPDesktop::clearWaitingCursor()
1181   if (waiting_cursor)
1182       sp_event_context_update_cursor(sp_desktop_event_context(this));
1185 void SPDesktop::toggleGrids()
1187     if (namedview->grids) {
1188         if(gridgroup) {
1189             showGrids(!grids_visible);
1190         }
1191     } else {
1192         //there is no grid present at the moment. add a rectangular grid and make it visible
1193         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1194         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1195         showGrids(true);
1196     }
1199 void SPDesktop::showGrids(bool show, bool dirty_document)
1201     grids_visible = show;
1202     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1203     if (show) {
1204         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1205     } else {
1206         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1207     }
1211 //----------------------------------------------------------------------
1212 // Callback implementations. The virtual ones are connected by the view.
1214 void
1215 SPDesktop::onPositionSet (double x, double y)
1217     _widget->viewSetPosition (NR::Point(x,y));
1220 void
1221 SPDesktop::onResized (double /*x*/, double /*y*/)
1223    // Nothing called here
1226 /**
1227  * Redraw callback; queues Gtk redraw; connected by View.
1228  */
1229 void
1230 SPDesktop::onRedrawRequested ()
1232     if (main) {
1233         _widget->requestCanvasUpdate();
1234     }
1237 void
1238 SPDesktop::updateCanvasNow()
1240   _widget->requestCanvasUpdateAndWait();
1243 /**
1244  * Associate document with desktop.
1245  */
1246 void
1247 SPDesktop::setDocument (SPDocument *doc)
1249     if (this->doc() && doc) {
1250         namedview->hide(this);
1251         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1252     }
1254     if (_layer_hierarchy) {
1255         _layer_hierarchy->clear();
1256         delete _layer_hierarchy;
1257     }
1258     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1259     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1260     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1261     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1262     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1264     /* setup EventLog */
1265     event_log = new Inkscape::EventLog(doc);
1266     doc->addUndoObserver(*event_log);
1268     _commit_connection.disconnect();
1269     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1271     /// \todo fixme: This condition exists to make sure the code
1272     /// inside is NOT called on initialization, only on replacement. But there
1273     /// are surely more safe methods to accomplish this.
1274     // TODO since the comment had reversed logic, check the intent of this block of code:
1275     if (drawing) {
1276         NRArenaItem *ai = 0;
1278         namedview = sp_document_namedview (doc, NULL);
1279         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1280         number = namedview->getViewCount();
1282         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1283                 SP_CANVAS_ARENA (drawing)->arena,
1284                 dkey,
1285                 SP_ITEM_SHOW_DISPLAY);
1286         if (ai) {
1287             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1288             nr_arena_item_unref (ai);
1289         }
1290         namedview->show(this);
1291         /* Ugly hack */
1292         activate_guides (true);
1293         /* Ugly hack */
1294         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1295     }
1297     _document_replaced_signal.emit (this, doc);
1299     View::setDocument (doc);
1302 void
1303 SPDesktop::onStatusMessage
1304 (Inkscape::MessageType type, gchar const *message)
1306     if (_widget) {
1307         _widget->setMessage(type, message);
1308     }
1311 void
1312 SPDesktop::onDocumentURISet (gchar const* uri)
1314     _widget->setTitle(uri);
1317 /**
1318  * Resized callback.
1319  */
1320 void
1321 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1323     _doc2dt[5] = height;
1324     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1325     NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1326     SP_CTRLRECT(page)->setRectangle(a);
1327     SP_CTRLRECT(page_border)->setRectangle(a);
1331 void
1332 SPDesktop::_onActivate (SPDesktop* dt)
1334     if (!dt->_widget) return;
1335     dt->_widget->activateDesktop();
1338 void
1339 SPDesktop::_onDeactivate (SPDesktop* dt)
1341     if (!dt->_widget) return;
1342     dt->_widget->deactivateDesktop();
1345 void
1346 SPDesktop::_onSelectionModified
1347 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1349     if (!dt->_widget) return;
1350     dt->_widget->updateScrollbars (expansion(dt->_d2w));
1353 static void
1354 _onSelectionChanged
1355 (Inkscape::Selection *selection, SPDesktop *desktop)
1357     /** \todo
1358      * only change the layer for single selections, or what?
1359      * This seems reasonable -- for multiple selections there can be many
1360      * different layers involved.
1361      */
1362     SPItem *item=selection->singleItem();
1363     if (item) {
1364         SPObject *layer=desktop->layerForObject(item);
1365         if ( layer && layer != desktop->currentLayer() ) {
1366             desktop->setCurrentLayer(layer);
1367         }
1368     }
1371 /**
1372  * Calls event handler of current event context.
1373  * \param arena Unused
1374  * \todo fixme
1375  */
1376 static gint
1377 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1379     if (ai) {
1380         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1381         return sp_event_context_item_handler (desktop->event_context, spi, event);
1382     } else {
1383         return sp_event_context_root_handler (desktop->event_context, event);
1384     }
1387 static void
1388 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1389     g_return_if_fail(SP_IS_GROUP(layer));
1390     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1393 /// Callback
1394 static void
1395 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1396     g_return_if_fail(SP_IS_GROUP(layer));
1397     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1400 /// Callback
1401 static void
1402 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1403                                          SPDesktop *desktop)
1405     desktop->_layer_changed_signal.emit (bottom);
1408 /// Called when document is starting to be rebuilt.
1409 static void
1410 _reconstruction_start (SPDesktop * desktop)
1412     // printf("Desktop, starting reconstruction\n");
1413     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1414     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1416     /*
1417     GSList const * selection_objs = desktop->selection->list();
1418     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1420     }
1421     */
1422     desktop->selection->clear();
1424     // printf("Desktop, starting reconstruction end\n");
1427 /// Called when document rebuild is finished.
1428 static void
1429 _reconstruction_finish (SPDesktop * desktop)
1431     // printf("Desktop, finishing reconstruction\n");
1432     if (desktop->_reconstruction_old_layer_id == NULL)
1433         return;
1435     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1436     if (newLayer != NULL)
1437         desktop->setCurrentLayer(newLayer);
1439     g_free(desktop->_reconstruction_old_layer_id);
1440     desktop->_reconstruction_old_layer_id = NULL;
1441     // printf("Desktop, finishing reconstruction end\n");
1442     return;
1445 /**
1446  * Namedview_modified callback.
1447  */
1448 static void
1449 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1451     SPNamedView *nv=SP_NAMEDVIEW(obj);
1453     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1455         /* Recalculate snap distances */
1456         /* FIXME: why is the desktop getting involved in setting up something
1457         ** that is entirely to do with the namedview?
1458         */
1459         _update_snap_distances (desktop);
1461         /* Show/hide page background */
1462         if (nv->pagecolor & 0xff) {
1463             sp_canvas_item_show (desktop->table);
1464             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1465             sp_canvas_item_move_to_z (desktop->table, 0);
1466         } else {
1467             sp_canvas_item_hide (desktop->table);
1468         }
1470         /* Show/hide page border */
1471         if (nv->showborder) {
1472             // show
1473             sp_canvas_item_show (desktop->page_border);
1474             // set color and shadow
1475             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1476             if (nv->pageshadow) {
1477                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1478             }
1479             // place in the z-order stack
1480             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1481                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1482             } else {
1483                 int order = sp_canvas_item_order (desktop->page_border);
1484                 int morder = sp_canvas_item_order (desktop->drawing);
1485                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1486                                     morder - order);
1487             }
1488         } else {
1489                 sp_canvas_item_hide (desktop->page_border);
1490                 if (nv->pageshadow) {
1491                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1492                 }
1493         }
1494         
1495         /* Show/hide page shadow */
1496         if (nv->showpageshadow && nv->pageshadow) {
1497             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1498         } else {
1499             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1500         }
1502         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1503             (SP_RGBA32_R_U(nv->pagecolor) +
1504              SP_RGBA32_G_U(nv->pagecolor) +
1505              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1506             // the background color is light or transparent, use black outline
1507             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1508         } else { // use white outline
1509             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1510         }
1511     }
1514 /**
1515  * Callback to reset snapper's distances.
1516  */
1517 static void
1518 _update_snap_distances (SPDesktop *desktop)
1520     SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1522     SPNamedView &nv = *desktop->namedview;
1524     //tell all grid snappers
1525     for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1526         Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1527         grid->snapper->setDistance(sp_convert_distance_full(nv.gridtolerance,
1528                                                                       *nv.gridtoleranceunit,
1529                                                                       px));
1530     }
1531     
1532     nv.snap_manager.guide.setDistance(sp_convert_distance_full(nv.guidetolerance,
1533                                                                        *nv.guidetoleranceunit,
1534                                                                        px));
1535     nv.snap_manager.object.setDistance(sp_convert_distance_full(nv.objecttolerance,
1536                                                                         *nv.objecttoleranceunit,
1537                                                                         px));
1541 NR::Matrix SPDesktop::w2d() const
1543     return _w2d;
1546 NR::Point SPDesktop::w2d(NR::Point const &p) const
1548     return p * _w2d;
1551 NR::Point SPDesktop::d2w(NR::Point const &p) const
1553     return p * _d2w;
1556 NR::Matrix SPDesktop::doc2dt() const
1558     return _doc2dt;
1561 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1563     return p * _doc2dt;
1566 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1568     return p / _doc2dt;
1572 /**
1573  * Pop event context from desktop's context stack. Never used.
1574  */
1575 // void
1576 // SPDesktop::pop_event_context (unsigned int key)
1577 // {
1578 //    SPEventContext *ec = NULL;
1579 //
1580 //    if (event_context && event_context->key == key) {
1581 //        g_return_if_fail (event_context);
1582 //        g_return_if_fail (event_context->next);
1583 //        ec = event_context;
1584 //        sp_event_context_deactivate (ec);
1585 //        event_context = ec->next;
1586 //        sp_event_context_activate (event_context);
1587 //        _event_context_changed_signal.emit (this, ec);
1588 //    }
1589 //
1590 //    SPEventContext *ref = event_context;
1591 //    while (ref && ref->next && ref->next->key != key)
1592 //        ref = ref->next;
1593 //
1594 //    if (ref && ref->next) {
1595 //        ec = ref->next;
1596 //        ref->next = ec->next;
1597 //    }
1598 //
1599 //    if (ec) {
1600 //        sp_event_context_finish (ec);
1601 //        g_object_unref (G_OBJECT (ec));
1602 //    }
1603 // }
1605 /*
1606   Local Variables:
1607   mode:c++
1608   c-file-style:"stroustrup"
1609   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1610   indent-tabs-mode:nil
1611   fill-column:99
1612   End:
1613 */
1614 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :