Code

2nd part of the fix for bug #167500: correctly update the rulers when they change...
[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 = &Inkscape::UI::Dialog::DialogManager::getInstance();
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     namedview->hide(this);
319     _activate_connection.disconnect();
320     _deactivate_connection.disconnect();
321     _sel_modified_connection.disconnect();
322     _sel_changed_connection.disconnect();
323     _modified_connection.disconnect();
324     _commit_connection.disconnect();
325     _reconstruction_start_connection.disconnect();
326     _reconstruction_finish_connection.disconnect();
328     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
329     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
330     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
332     while (event_context) {
333         SPEventContext *ec = event_context;
334         event_context = ec->next;
335         sp_event_context_finish (ec);
336         g_object_unref (G_OBJECT (ec));
337     }
339     if (_layer_hierarchy) {
340         delete _layer_hierarchy;
341 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
342     }
344     if (layer_manager) {
345         delete layer_manager;
346         layer_manager = NULL;
347     }
349     if (_inkscape) {
350         _inkscape = NULL;
351     }
353     if (drawing) {
354         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
355         drawing = NULL;
356     }
358     delete _guides_message_context;
359     _guides_message_context = NULL;
361     g_list_free (zooms_past);
362     g_list_free (zooms_future);
365 SPDesktop::~SPDesktop() {}
367 //--------------------------------------------------------------------
368 /* Public methods */
370 void SPDesktop::setDisplayModeNormal()
372     SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
373     canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
374     displayMode = RENDERMODE_NORMAL;
375     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
376     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
379 void SPDesktop::setDisplayModeOutline()
381     SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
382     canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
383     displayMode = RENDERMODE_OUTLINE;
384     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
385     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
388 void SPDesktop::displayModeToggle()
390     if (displayMode == RENDERMODE_OUTLINE)
391         setDisplayModeNormal();
392     else
393         setDisplayModeOutline();
396 /**
397  * Returns current root (=bottom) layer.
398  */
399 SPObject *SPDesktop::currentRoot() const
401     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
404 /**
405  * Returns current top layer.
406  */
407 SPObject *SPDesktop::currentLayer() const
409     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
412 /**
413  * Sets the current layer of the desktop.
414  *
415  * Make \a object the top layer.
416  */
417 void SPDesktop::setCurrentLayer(SPObject *object) {
418     g_return_if_fail(SP_IS_GROUP(object));
419     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
420     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
421     _layer_hierarchy->setBottom(object);
424 /**
425  * Return layer that contains \a object.
426  */
427 SPObject *SPDesktop::layerForObject(SPObject *object) {
428     g_return_val_if_fail(object != NULL, NULL);
430     SPObject *root=currentRoot();
431     object = SP_OBJECT_PARENT(object);
432     while ( object && object != root && !isLayer(object) ) {
433         object = SP_OBJECT_PARENT(object);
434     }
435     return object;
438 /**
439  * True if object is a layer.
440  */
441 bool SPDesktop::isLayer(SPObject *object) const {
442     return ( SP_IS_GROUP(object)
443              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
444                   == SPGroup::LAYER ) );
447 /**
448  * True if desktop viewport fully contains \a item's bbox.
449  */
450 bool SPDesktop::isWithinViewport (SPItem *item) const
452     NR::Rect const viewport = get_display_area();
453     NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
454     if (bbox) {
455         return viewport.contains(*bbox);
456     } else {
457         return true;
458     }
461 ///
462 bool SPDesktop::itemIsHidden(SPItem const *item) const {
463     return item->isHidden(this->dkey);
466 /**
467  * Set activate property of desktop; emit signal if changed.
468  */
469 void
470 SPDesktop::set_active (bool new_active)
472     if (new_active != _active) {
473         _active = new_active;
474         if (new_active) {
475             _activate_signal.emit();
476         } else {
477             _deactivate_signal.emit();
478         }
479     }
482 /**
483  * Set activate status of current desktop's named view.
484  */
485 void
486 SPDesktop::activate_guides(bool activate)
488     guides_active = activate;
489     namedview->activateGuides(this, activate);
492 /**
493  * Make desktop switch documents.
494  */
495 void
496 SPDesktop::change_document (SPDocument *theDocument)
498     g_return_if_fail (theDocument != NULL);
500     /* unselect everything before switching documents */
501     selection->clear();
503     setDocument (theDocument);
504     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
505     _document_replaced_signal.emit (this, theDocument);
508 /**
509  * Make desktop switch event contexts.
510  */
511 void
512 SPDesktop::set_event_context (GtkType type, const gchar *config)
514     SPEventContext *ec;
515     while (event_context) {
516         ec = event_context;
517         sp_event_context_deactivate (ec);
518         event_context = ec->next;
519         sp_event_context_finish (ec);
520         g_object_unref (G_OBJECT (ec));
521     }
523     Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
524     ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
525     ec->next = event_context;
526     event_context = ec;
527     sp_event_context_activate (ec);
528     _event_context_changed_signal.emit (this, ec);
531 /**
532  * Push event context onto desktop's context stack.
533  */
534 void
535 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
537     SPEventContext *ref, *ec;
538     Inkscape::XML::Node *repr;
540     if (event_context && event_context->key == key) return;
541     ref = event_context;
542     while (ref && ref->next && ref->next->key != key) ref = ref->next;
543     if (ref && ref->next) {
544         ec = ref->next;
545         ref->next = ec->next;
546         sp_event_context_finish (ec);
547         g_object_unref (G_OBJECT (ec));
548     }
550     if (event_context) sp_event_context_deactivate (event_context);
551     repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
552     ec = sp_event_context_new (type, this, repr, key);
553     ec->next = event_context;
554     event_context = ec;
555     sp_event_context_activate (ec);
556     _event_context_changed_signal.emit (this, ec);
559 /**
560  * Sets the coordinate status to a given point
561  */
562 void
563 SPDesktop::set_coordinate_status (NR::Point p) {
564     _widget->setCoordinateStatus(p);
567 /**
568  * \see sp_document_item_from_list_at_point_bottom()
569  */
570 SPItem *
571 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
573     g_return_val_if_fail (doc() != NULL, NULL);
574     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
577 /**
578  * \see sp_document_item_at_point()
579  */
580 SPItem *
581 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
583     g_return_val_if_fail (doc() != NULL, NULL);
584     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
587 /**
588  * \see sp_document_group_at_point()
589  */
590 SPItem *
591 SPDesktop::group_at_point (NR::Point const p) const
593     g_return_val_if_fail (doc() != NULL, NULL);
594     return sp_document_group_at_point (doc(), dkey, p);
597 /**
598  * \brief  Returns the mouse point in document coordinates; if mouse is
599  * outside the canvas, returns the center of canvas viewpoint
600  */
601 NR::Point
602 SPDesktop::point() const
604     NR::Point p = _widget->getPointer();
605     NR::Point pw = sp_canvas_window_to_world (canvas, p);
606     p = w2d(pw);
608     NR::Rect const r = canvas->getViewbox();
610     NR::Point r0 = w2d(r.min());
611     NR::Point r1 = w2d(r.max());
613     if (p[NR::X] >= r0[NR::X] &&
614         p[NR::X] <= r1[NR::X] &&
615         p[NR::Y] >= r1[NR::Y] &&
616         p[NR::Y] <= r0[NR::Y])
617     {
618         return p;
619     } else {
620         return (r0 + r1) / 2;
621     }
624 /**
625  * Put current zoom data in history list.
626  */
627 void
628 SPDesktop::push_current_zoom (GList **history)
630     NR::Rect const area = get_display_area();
632     NRRect *old_zoom = g_new(NRRect, 1);
633     old_zoom->x0 = area.min()[NR::X];
634     old_zoom->x1 = area.max()[NR::X];
635     old_zoom->y0 = area.min()[NR::Y];
636     old_zoom->y1 = area.max()[NR::Y];
637     if ( *history == NULL
638          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
639                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
640                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
641                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
642     {
643         *history = g_list_prepend (*history, old_zoom);
644     }
647 /**
648  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
649  */
650 void
651 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
653     g_assert(_widget);
655     // save the zoom
656     if (log) {
657         push_current_zoom(&zooms_past);
658         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
659         g_list_free (zooms_future);
660         zooms_future = NULL;
661     }
663     double const cx = 0.5 * (x0 + x1);
664     double const cy = 0.5 * (y0 + y1);
666     NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
668     double scale = expansion(_d2w);
669     double newscale;
670     if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
671         newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
672     } else {
673         newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
674     }
676     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
678     int clear = FALSE;
679     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
680         /* Set zoom factors */
681         _d2w = NR::Matrix(NR::scale(newscale, -newscale));
682         _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
683         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
684         clear = TRUE;
685     }
687     /* Calculate top left corner (in document pixels) */
688     x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
689     y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
691     /* Scroll */
692     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
694     _widget->updateRulers();
695     _widget->updateScrollbars(expansion(_d2w));
696     _widget->updateZoom();
699 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
701     set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
704 /**
705  * Return viewbox dimensions.
706  */
707 NR::Rect SPDesktop::get_display_area() const
709     NR::Rect const viewbox = canvas->getViewbox();
711     double const scale = _d2w[0];
713     return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
714                     NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
717 /**
718  * Revert back to previous zoom if possible.
719  */
720 void
721 SPDesktop::prev_zoom()
723     if (zooms_past == NULL) {
724         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
725         return;
726     }
728     // push current zoom into forward zooms list
729     push_current_zoom (&zooms_future);
731     // restore previous zoom
732     set_display_area (((NRRect *) zooms_past->data)->x0,
733             ((NRRect *) zooms_past->data)->y0,
734             ((NRRect *) zooms_past->data)->x1,
735             ((NRRect *) zooms_past->data)->y1,
736             0, false);
738     // remove the just-added zoom from the past zooms list
739     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
742 /**
743  * Set zoom to next in list.
744  */
745 void
746 SPDesktop::next_zoom()
748     if (zooms_future == NULL) {
749         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
750         return;
751     }
753     // push current zoom into past zooms list
754     push_current_zoom (&zooms_past);
756     // restore next zoom
757     set_display_area (((NRRect *) zooms_future->data)->x0,
758             ((NRRect *) zooms_future->data)->y0,
759             ((NRRect *) zooms_future->data)->x1,
760             ((NRRect *) zooms_future->data)->y1,
761             0, false);
763     // remove the just-used zoom from the zooms_future list
764     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
767 /**
768  * Zoom to point with absolute zoom factor.
769  */
770 void
771 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
773     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
775     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
776     // this check prevents "sliding" when trying to zoom in at maximum zoom;
777     /// \todo someone please fix calculations properly and remove this hack
778     if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
779         return;
781     NR::Rect const viewbox = canvas->getViewbox();
783     double const width2 = viewbox.dimensions()[NR::X] / zoom;
784     double const height2 = viewbox.dimensions()[NR::Y] / zoom;
786     set_display_area(cx - px * width2,
787                      cy - py * height2,
788                      cx + (1 - px) * width2,
789                      cy + (1 - py) * height2,
790                      0.0);
793 /**
794  * Zoom to center with absolute zoom factor.
795  */
796 void
797 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
799     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
802 /**
803  * Zoom to point with relative zoom factor.
804  */
805 void
806 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
808     NR::Rect const area = get_display_area();
810     if (cx < area.min()[NR::X]) {
811         cx = area.min()[NR::X];
812     }
813     if (cx > area.max()[NR::X]) {
814         cx = area.max()[NR::X];
815     }
816     if (cy < area.min()[NR::Y]) {
817         cy = area.min()[NR::Y];
818     }
819     if (cy > area.max()[NR::Y]) {
820         cy = area.max()[NR::Y];
821     }
823     gdouble const scale = expansion(_d2w) * zoom;
824     double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
825     double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
827     zoom_absolute_keep_point(cx, cy, px, py, scale);
830 /**
831  * Zoom to center with relative zoom factor.
832  */
833 void
834 SPDesktop::zoom_relative (double cx, double cy, double zoom)
836     gdouble scale = expansion(_d2w) * zoom;
837     zoom_absolute (cx, cy, scale);
840 /**
841  * Set display area to origin and current document dimensions.
842  */
843 void
844 SPDesktop::zoom_page()
846     NR::Rect d(NR::Point(0, 0),
847                NR::Point(sp_document_width(doc()), sp_document_height(doc())));
849     if (d.isEmpty(1.0)) {
850         return;
851     }
853     set_display_area(d, 10);
856 /**
857  * Set display area to current document width.
858  */
859 void
860 SPDesktop::zoom_page_width()
862     NR::Rect const a = get_display_area();
864     if (sp_document_width(doc()) < 1.0) {
865         return;
866     }
868     NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
869                NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
871     set_display_area(d, 10);
874 /**
875  * Zoom to selection.
876  */
877 void
878 SPDesktop::zoom_selection()
880     NR::Maybe<NR::Rect> const d = selection->bounds();
882     if ( !d || d->isEmpty(0.1) ) {
883         return;
884     }
886     set_display_area(*d, 10);
889 /**
890  * Tell widget to let zoom widget grab keyboard focus.
891  */
892 void
893 SPDesktop::zoom_grab_focus()
895     _widget->letZoomGrabFocus();
898 /**
899  * Zoom to whole drawing.
900  */
901 void
902 SPDesktop::zoom_drawing()
904     g_return_if_fail (doc() != NULL);
905     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
906     g_return_if_fail (docitem != NULL);
908     NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
910     /* Note that the second condition here indicates that
911     ** there are no items in the drawing.
912     */
913     if ( !d || d->isEmpty(1.0) ) {
914         return;
915     }
917     set_display_area(*d, 10);
920 /**
921  * Scroll canvas by specific coordinate amount.
922  */
923 void
924 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
926     g_assert(_widget);
928     NR::Rect const viewbox = canvas->getViewbox();
930     sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
932     _widget->updateRulers();
933     _widget->updateScrollbars(expansion(_d2w));
936 bool
937 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
939     gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
941     // autoscrolldistance is in screen pixels, but the display area is in document units
942     autoscrolldistance /= expansion(_d2w);
943     NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
945     if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
946         !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y])   ) {
948         NR::Point const s_w( (*p) * _d2w );
950         gdouble x_to;
951         if ((*p)[NR::X] < dbox.min()[NR::X])
952             x_to = dbox.min()[NR::X];
953         else if ((*p)[NR::X] > dbox.max()[NR::X])
954             x_to = dbox.max()[NR::X];
955         else
956             x_to = (*p)[NR::X];
958         gdouble y_to;
959         if ((*p)[NR::Y] < dbox.min()[NR::Y])
960             y_to = dbox.min()[NR::Y];
961         else if ((*p)[NR::Y] > dbox.max()[NR::Y])
962             y_to = dbox.max()[NR::Y];
963         else
964             y_to = (*p)[NR::Y];
966         NR::Point const d_dt(x_to, y_to);
967         NR::Point const d_w( d_dt * _d2w );
968         NR::Point const moved_w( d_w - s_w );
970         if (autoscrollspeed == 0)
971             autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
973         if (autoscrollspeed != 0)
974             scroll_world (autoscrollspeed * moved_w);
976         return true;
977     }
978     return false;
981 bool
982 SPDesktop::is_iconified()
984     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
987 void
988 SPDesktop::iconify()
990     _widget->setIconified();
993 bool
994 SPDesktop::is_maximized()
996     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
999 void
1000 SPDesktop::maximize()
1002     _widget->setMaximized();
1005 bool
1006 SPDesktop::is_fullscreen()
1008     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1011 void
1012 SPDesktop::fullscreen()
1014     _widget->setFullscreen();
1017 void
1018 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1020     _widget->getGeometry (x, y, w, h);
1023 void
1024 SPDesktop::setWindowPosition (NR::Point p)
1026     _widget->setPosition (p);
1029 void
1030 SPDesktop::setWindowSize (gint w, gint h)
1032     _widget->setSize (w, h);
1035 void
1036 SPDesktop::setWindowTransient (void *p, int transient_policy)
1038     _widget->setTransient (p, transient_policy);
1041 Gtk::Window*
1042 SPDesktop::getToplevel( )
1044     return _widget->getWindow();
1047 void
1048 SPDesktop::presentWindow()
1050     _widget->present();
1053 bool
1054 SPDesktop::warnDialog (gchar *text)
1056     return _widget->warnDialog (text);
1059 void
1060 SPDesktop::toggleRulers()
1062     _widget->toggleRulers();
1065 void
1066 SPDesktop::toggleScrollbars()
1068     _widget->toggleScrollbars();
1071 void
1072 SPDesktop::layoutWidget()
1074     _widget->layout();
1077 void
1078 SPDesktop::destroyWidget()
1080     _widget->destroy();
1083 bool
1084 SPDesktop::shutdown()
1086     return _widget->shutdown();
1089 bool SPDesktop::onDeleteUI (GdkEventAny*)
1091     if(shutdown()) 
1092         return true;
1094     destroyWidget();
1095     return false;
1098 /**
1099  *  onWindowStateEvent
1100  *
1101  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1102  *  Since GTK doesn't have a way to query this state information directly, we
1103  *  record it for the desktop here, and also possibly trigger a layout.
1104  */
1105 bool
1106 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1108         // Record the desktop window's state
1109     window_state = event->new_window_state;
1111     // Layout may differ depending on full-screen mode or not
1112     GdkWindowState changed = event->changed_mask;
1113     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1114         layoutWidget();
1115     }
1117         return false;
1120 void
1121 SPDesktop::setToolboxFocusTo (gchar const *label)
1123     _widget->setToolboxFocusTo (label);
1126 void
1127 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1129     _widget->setToolboxAdjustmentValue (id, val);
1132 void
1133 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1135     _widget->setToolboxSelectOneValue (id, val);
1138 bool
1139 SPDesktop::isToolboxButtonActive (gchar const *id)
1141     return _widget->isToolboxButtonActive (id);
1144 void
1145 SPDesktop::emitToolSubselectionChanged(gpointer data)
1147         _tool_subselection_changed.emit(data);
1148         inkscape_subselection_changed (this);
1151 void
1152 SPDesktop::updateNow()
1154   sp_canvas_update_now(canvas);
1157 void
1158 SPDesktop::enableInteraction()
1160   _widget->enableInteraction();
1163 void SPDesktop::disableInteraction()
1165   _widget->disableInteraction();
1168 void SPDesktop::setWaitingCursor()
1170     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1171     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1172     gdk_cursor_unref(waiting);
1173     waiting_cursor = true;
1175     // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1176     // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1177     // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1178     // after the call to setWaitingCursor as it was before
1179     while( Gtk::Main::events_pending() )
1180        Gtk::Main::iteration();
1183 void SPDesktop::clearWaitingCursor()
1185   if (waiting_cursor)
1186       sp_event_context_update_cursor(sp_desktop_event_context(this));
1189 void SPDesktop::toggleColorProfAdjust()
1191     _widget->toggleColorProfAdjust();
1194 void SPDesktop::toggleGrids()
1196     if (namedview->grids) {
1197         if(gridgroup) {
1198             showGrids(!grids_visible);
1199         }
1200     } else {
1201         //there is no grid present at the moment. add a rectangular grid and make it visible
1202         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1203         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1204         showGrids(true);
1205     }
1208 void SPDesktop::showGrids(bool show, bool dirty_document)
1210     grids_visible = show;
1211     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1212     if (show) {
1213         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1214     } else {
1215         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1216     }
1219 void SPDesktop::toggleSnapping()
1221     bool v = namedview->snap_manager.getSnapEnabledGlobally();
1222     Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1223     sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1226 //----------------------------------------------------------------------
1227 // Callback implementations. The virtual ones are connected by the view.
1229 void
1230 SPDesktop::onPositionSet (double x, double y)
1232     _widget->viewSetPosition (NR::Point(x,y));
1235 void
1236 SPDesktop::onResized (double /*x*/, double /*y*/)
1238    // Nothing called here
1241 /**
1242  * Redraw callback; queues Gtk redraw; connected by View.
1243  */
1244 void
1245 SPDesktop::onRedrawRequested ()
1247     if (main) {
1248         _widget->requestCanvasUpdate();
1249     }
1252 void
1253 SPDesktop::updateCanvasNow()
1255   _widget->requestCanvasUpdateAndWait();
1258 /**
1259  * Associate document with desktop.
1260  */
1261 void
1262 SPDesktop::setDocument (SPDocument *doc)
1264     if (this->doc() && doc) {
1265         namedview->hide(this);
1266         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1267     }
1269     if (_layer_hierarchy) {
1270         _layer_hierarchy->clear();
1271         delete _layer_hierarchy;
1272     }
1273     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1274     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1275     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1276     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1277     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1279     /* setup EventLog */
1280     event_log = new Inkscape::EventLog(doc);
1281     doc->addUndoObserver(*event_log);
1283     _commit_connection.disconnect();
1284     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1286     /// \todo fixme: This condition exists to make sure the code
1287     /// inside is NOT called on initialization, only on replacement. But there
1288     /// are surely more safe methods to accomplish this.
1289     // TODO since the comment had reversed logic, check the intent of this block of code:
1290     if (drawing) {
1291         NRArenaItem *ai = 0;
1293         namedview = sp_document_namedview (doc, NULL);
1294         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1295         number = namedview->getViewCount();
1297         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1298                 SP_CANVAS_ARENA (drawing)->arena,
1299                 dkey,
1300                 SP_ITEM_SHOW_DISPLAY);
1301         if (ai) {
1302             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1303             nr_arena_item_unref (ai);
1304         }
1305         namedview->show(this);
1306         /* Ugly hack */
1307         activate_guides (true);
1308         /* Ugly hack */
1309         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1310     }
1312     _document_replaced_signal.emit (this, doc);
1314     View::setDocument (doc);
1317 void
1318 SPDesktop::onStatusMessage
1319 (Inkscape::MessageType type, gchar const *message)
1321     if (_widget) {
1322         _widget->setMessage(type, message);
1323     }
1326 void
1327 SPDesktop::onDocumentURISet (gchar const* uri)
1329     _widget->setTitle(uri);
1332 /**
1333  * Resized callback.
1334  */
1335 void
1336 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1338     _doc2dt[5] = height;
1339     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1340     NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1341     SP_CTRLRECT(page)->setRectangle(a);
1342     SP_CTRLRECT(page_border)->setRectangle(a);
1346 void
1347 SPDesktop::_onActivate (SPDesktop* dt)
1349     if (!dt->_widget) return;
1350     dt->_widget->activateDesktop();
1353 void
1354 SPDesktop::_onDeactivate (SPDesktop* dt)
1356     if (!dt->_widget) return;
1357     dt->_widget->deactivateDesktop();
1360 void
1361 SPDesktop::_onSelectionModified
1362 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1364     if (!dt->_widget) return;
1365     dt->_widget->updateScrollbars (expansion(dt->_d2w));
1368 static void
1369 _onSelectionChanged
1370 (Inkscape::Selection *selection, SPDesktop *desktop)
1372     /** \todo
1373      * only change the layer for single selections, or what?
1374      * This seems reasonable -- for multiple selections there can be many
1375      * different layers involved.
1376      */
1377     SPItem *item=selection->singleItem();
1378     if (item) {
1379         SPObject *layer=desktop->layerForObject(item);
1380         if ( layer && layer != desktop->currentLayer() ) {
1381             desktop->setCurrentLayer(layer);
1382         }
1383     }
1386 /**
1387  * Calls event handler of current event context.
1388  * \param arena Unused
1389  * \todo fixme
1390  */
1391 static gint
1392 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1394     if (ai) {
1395         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1396         return sp_event_context_item_handler (desktop->event_context, spi, event);
1397     } else {
1398         return sp_event_context_root_handler (desktop->event_context, event);
1399     }
1402 static void
1403 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1404     g_return_if_fail(SP_IS_GROUP(layer));
1405     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1408 /// Callback
1409 static void
1410 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1411     g_return_if_fail(SP_IS_GROUP(layer));
1412     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1415 /// Callback
1416 static void
1417 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1418                                          SPDesktop *desktop)
1420     desktop->_layer_changed_signal.emit (bottom);
1423 /// Called when document is starting to be rebuilt.
1424 static void
1425 _reconstruction_start (SPDesktop * desktop)
1427     // printf("Desktop, starting reconstruction\n");
1428     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1429     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1431     /*
1432     GSList const * selection_objs = desktop->selection->list();
1433     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1435     }
1436     */
1437     desktop->selection->clear();
1439     // printf("Desktop, starting reconstruction end\n");
1442 /// Called when document rebuild is finished.
1443 static void
1444 _reconstruction_finish (SPDesktop * desktop)
1446     // printf("Desktop, finishing reconstruction\n");
1447     if (desktop->_reconstruction_old_layer_id == NULL)
1448         return;
1450     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1451     if (newLayer != NULL)
1452         desktop->setCurrentLayer(newLayer);
1454     g_free(desktop->_reconstruction_old_layer_id);
1455     desktop->_reconstruction_old_layer_id = NULL;
1456     // printf("Desktop, finishing reconstruction end\n");
1457     return;
1460 /**
1461  * Namedview_modified callback.
1462  */
1463 static void
1464 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1466     SPNamedView *nv=SP_NAMEDVIEW(obj);
1468     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1470         /* Recalculate snap distances */
1471         /* FIXME: why is the desktop getting involved in setting up something
1472         ** that is entirely to do with the namedview?
1473         */
1474         _update_snap_distances (desktop);
1476         /* Show/hide page background */
1477         if (nv->pagecolor & 0xff) {
1478             sp_canvas_item_show (desktop->table);
1479             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1480             sp_canvas_item_move_to_z (desktop->table, 0);
1481         } else {
1482             sp_canvas_item_hide (desktop->table);
1483         }
1485         /* Show/hide page border */
1486         if (nv->showborder) {
1487             // show
1488             sp_canvas_item_show (desktop->page_border);
1489             // set color and shadow
1490             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1491             if (nv->pageshadow) {
1492                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1493             }
1494             // place in the z-order stack
1495             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1496                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1497             } else {
1498                 int order = sp_canvas_item_order (desktop->page_border);
1499                 int morder = sp_canvas_item_order (desktop->drawing);
1500                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1501                                     morder - order);
1502             }
1503         } else {
1504                 sp_canvas_item_hide (desktop->page_border);
1505                 if (nv->pageshadow) {
1506                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1507                 }
1508         }
1510         /* Show/hide page shadow */
1511         if (nv->showpageshadow && nv->pageshadow) {
1512             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1513         } else {
1514             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1515         }
1517         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1518             (SP_RGBA32_R_U(nv->pagecolor) +
1519              SP_RGBA32_G_U(nv->pagecolor) +
1520              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1521             // the background color is light or transparent, use black outline
1522             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1523         } else { // use white outline
1524             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1525         }
1526     }
1529 /**
1530  * Callback to reset snapper's distances.
1531  */
1532 static void
1533 _update_snap_distances (SPDesktop *desktop)
1535     SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1537     SPNamedView &nv = *desktop->namedview;
1539     //tell all grid snappers
1540     for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1541         Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1542         grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
1543                                                                       *nv.gridtoleranceunit,
1544                                                                       px));
1545     }
1547     nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
1548                                                                        *nv.guidetoleranceunit,
1549                                                                        px));
1550     nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
1551                                                                         *nv.objecttoleranceunit,
1552                                                                         px));
1556 NR::Matrix SPDesktop::w2d() const
1558     return _w2d;
1561 NR::Point SPDesktop::w2d(NR::Point const &p) const
1563     return p * _w2d;
1566 NR::Point SPDesktop::d2w(NR::Point const &p) const
1568     return p * _d2w;
1571 NR::Matrix SPDesktop::doc2dt() const
1573     return _doc2dt;
1576 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1578     return p * _doc2dt;
1581 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1583     return p / _doc2dt;
1587 /**
1588  * Pop event context from desktop's context stack. Never used.
1589  */
1590 // void
1591 // SPDesktop::pop_event_context (unsigned int key)
1592 // {
1593 //    SPEventContext *ec = NULL;
1594 //
1595 //    if (event_context && event_context->key == key) {
1596 //        g_return_if_fail (event_context);
1597 //        g_return_if_fail (event_context->next);
1598 //        ec = event_context;
1599 //        sp_event_context_deactivate (ec);
1600 //        event_context = ec->next;
1601 //        sp_event_context_activate (event_context);
1602 //        _event_context_changed_signal.emit (this, ec);
1603 //    }
1604 //
1605 //    SPEventContext *ref = event_context;
1606 //    while (ref && ref->next && ref->next->key != key)
1607 //        ref = ref->next;
1608 //
1609 //    if (ref && ref->next) {
1610 //        ec = ref->next;
1611 //        ref->next = ec->next;
1612 //    }
1613 //
1614 //    if (ec) {
1615 //        sp_event_context_finish (ec);
1616 //        g_object_unref (G_OBJECT (ec));
1617 //    }
1618 // }
1620 /*
1621   Local Variables:
1622   mode:c++
1623   c-file-style:"stroustrup"
1624   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1625   indent-tabs-mode:nil
1626   fill-column:99
1627   End:
1628 */
1629 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :