Code

Warning cleanup
[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);
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     }
341     if (_inkscape) {
342         _inkscape = NULL;
343     }
345     if (drawing) {
346         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
347         drawing = NULL;
348     }
350     delete _guides_message_context;
351     _guides_message_context = NULL;
353     g_list_free (zooms_past);
354     g_list_free (zooms_future);
357 SPDesktop::~SPDesktop() {}
359 //--------------------------------------------------------------------
360 /* Public methods */
362 void SPDesktop::setDisplayModeNormal()
364     SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
365     canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
366     displayMode = RENDERMODE_NORMAL;
367     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
368     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
371 void SPDesktop::setDisplayModeOutline()
373     SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
374     canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
375     displayMode = RENDERMODE_OUTLINE;
376     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
377     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
380 void SPDesktop::displayModeToggle()
382     if (displayMode == RENDERMODE_OUTLINE)
383         setDisplayModeNormal();
384     else 
385         setDisplayModeOutline();
388 /**
389  * Returns current root (=bottom) layer.
390  */
391 SPObject *SPDesktop::currentRoot() const
393     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
396 /**
397  * Returns current top layer.
398  */
399 SPObject *SPDesktop::currentLayer() const
401     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
404 /**
405  * Sets the current layer of the desktop.
406  * 
407  * Make \a object the top layer.
408  */
409 void SPDesktop::setCurrentLayer(SPObject *object) {
410     g_return_if_fail(SP_IS_GROUP(object));
411     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
412     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
413     _layer_hierarchy->setBottom(object);
416 /**
417  * Return layer that contains \a object.
418  */
419 SPObject *SPDesktop::layerForObject(SPObject *object) {
420     g_return_val_if_fail(object != NULL, NULL);
422     SPObject *root=currentRoot();
423     object = SP_OBJECT_PARENT(object);
424     while ( object && object != root && !isLayer(object) ) {
425         object = SP_OBJECT_PARENT(object);
426     }
427     return object;
430 /**
431  * True if object is a layer.
432  */
433 bool SPDesktop::isLayer(SPObject *object) const {
434     return ( SP_IS_GROUP(object)
435              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
436                   == SPGroup::LAYER ) );
439 /**
440  * True if desktop viewport fully contains \a item's bbox.
441  */
442 bool SPDesktop::isWithinViewport (SPItem *item) const
444     NR::Rect const viewport = get_display_area();
445     NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
446     if (bbox) {
447         return viewport.contains(*bbox);
448     } else {
449         return true;
450     }
453 ///
454 bool SPDesktop::itemIsHidden(SPItem const *item) const {
455     return item->isHidden(this->dkey);
458 /**
459  * Set activate property of desktop; emit signal if changed.
460  */
461 void
462 SPDesktop::set_active (bool new_active)
464     if (new_active != _active) {
465         _active = new_active;
466         if (new_active) {
467             _activate_signal.emit();
468         } else {
469             _deactivate_signal.emit();
470         }
471     }
474 /**
475  * Set activate status of current desktop's named view.
476  */
477 void
478 SPDesktop::activate_guides(bool activate)
480     guides_active = activate;
481     namedview->activateGuides(this, activate);
484 /**
485  * Make desktop switch documents.
486  */
487 void
488 SPDesktop::change_document (SPDocument *theDocument)
490     g_return_if_fail (theDocument != NULL);
492     /* unselect everything before switching documents */
493     selection->clear();
495     setDocument (theDocument);
496     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
497     _document_replaced_signal.emit (this, theDocument);
500 /**
501  * Make desktop switch event contexts.
502  */
503 void
504 SPDesktop::set_event_context (GtkType type, const gchar *config)
506     SPEventContext *ec;
507     while (event_context) {
508         ec = event_context;
509         sp_event_context_deactivate (ec);
510         event_context = ec->next;
511         sp_event_context_finish (ec);
512         g_object_unref (G_OBJECT (ec));
513     }
515     Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
516     ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
517     ec->next = event_context;
518     event_context = ec;
519     sp_event_context_activate (ec);
520     _event_context_changed_signal.emit (this, ec);
523 /**
524  * Push event context onto desktop's context stack.
525  */
526 void
527 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
529     SPEventContext *ref, *ec;
530     Inkscape::XML::Node *repr;
532     if (event_context && event_context->key == key) return;
533     ref = event_context;
534     while (ref && ref->next && ref->next->key != key) ref = ref->next;
535     if (ref && ref->next) {
536         ec = ref->next;
537         ref->next = ec->next;
538         sp_event_context_finish (ec);
539         g_object_unref (G_OBJECT (ec));
540     }
542     if (event_context) sp_event_context_deactivate (event_context);
543     repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
544     ec = sp_event_context_new (type, this, repr, key);
545     ec->next = event_context;
546     event_context = ec;
547     sp_event_context_activate (ec);
548     _event_context_changed_signal.emit (this, ec);
551 /**
552  * Sets the coordinate status to a given point
553  */
554 void
555 SPDesktop::set_coordinate_status (NR::Point p) {
556     _widget->setCoordinateStatus(p);
559 /**
560  * \see sp_document_item_from_list_at_point_bottom()
561  */
562 SPItem *
563 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
565     g_return_val_if_fail (doc() != NULL, NULL);
566     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
569 /**
570  * \see sp_document_item_at_point()
571  */
572 SPItem *
573 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
575     g_return_val_if_fail (doc() != NULL, NULL);
576     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
579 /**
580  * \see sp_document_group_at_point()
581  */
582 SPItem *
583 SPDesktop::group_at_point (NR::Point const p) const
585     g_return_val_if_fail (doc() != NULL, NULL);
586     return sp_document_group_at_point (doc(), dkey, p);
589 /**
590  * \brief  Returns the mouse point in document coordinates; if mouse is
591  * outside the canvas, returns the center of canvas viewpoint
592  */
593 NR::Point
594 SPDesktop::point() const
596     NR::Point p = _widget->getPointer();
597     NR::Point pw = sp_canvas_window_to_world (canvas, p);
598     p = w2d(pw);
600     NR::Rect const r = canvas->getViewbox();
602     NR::Point r0 = w2d(r.min());
603     NR::Point r1 = w2d(r.max());
605     if (p[NR::X] >= r0[NR::X] &&
606         p[NR::X] <= r1[NR::X] &&
607         p[NR::Y] >= r1[NR::Y] &&
608         p[NR::Y] <= r0[NR::Y])
609     {
610         return p;
611     } else {
612         return (r0 + r1) / 2;
613     }
616 /**
617  * Put current zoom data in history list.
618  */
619 void
620 SPDesktop::push_current_zoom (GList **history)
622     NR::Rect const area = get_display_area();
624     NRRect *old_zoom = g_new(NRRect, 1);
625     old_zoom->x0 = area.min()[NR::X];
626     old_zoom->x1 = area.max()[NR::X];
627     old_zoom->y0 = area.min()[NR::Y];
628     old_zoom->y1 = area.max()[NR::Y];
629     if ( *history == NULL
630          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
631                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
632                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
633                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
634     {
635         *history = g_list_prepend (*history, old_zoom);
636     }
639 /**
640  * Set viewbox.
641  */
642 void
643 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
645     g_assert(_widget);
647     // save the zoom
648     if (log) {
649         push_current_zoom(&zooms_past);
650         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
651         g_list_free (zooms_future);
652         zooms_future = NULL;
653     }
655     double const cx = 0.5 * (x0 + x1);
656     double const cy = 0.5 * (y0 + y1);
658     NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
660     double scale = expansion(_d2w);
661     double newscale;
662     if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
663         newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
664     } else {
665         newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
666     }
668     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
670     int clear = FALSE;
671     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
672         /* Set zoom factors */
673         _d2w = NR::Matrix(NR::scale(newscale, -newscale));
674         _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
675         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
676         clear = TRUE;
677     }
679     /* Calculate top left corner */
680     x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
681     y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
683     /* Scroll */
684     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
686     _widget->updateRulers();
687     _widget->updateScrollbars(expansion(_d2w));
688     _widget->updateZoom();
691 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
693     set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
696 /**
697  * Return viewbox dimensions.
698  */
699 NR::Rect SPDesktop::get_display_area() const
701     NR::Rect const viewbox = canvas->getViewbox();
703     double const scale = _d2w[0];
705     return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
706                     NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
709 /**
710  * Revert back to previous zoom if possible.
711  */
712 void
713 SPDesktop::prev_zoom()
715     if (zooms_past == NULL) {
716         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
717         return;
718     }
720     // push current zoom into forward zooms list
721     push_current_zoom (&zooms_future);
723     // restore previous zoom
724     set_display_area (((NRRect *) zooms_past->data)->x0,
725             ((NRRect *) zooms_past->data)->y0,
726             ((NRRect *) zooms_past->data)->x1,
727             ((NRRect *) zooms_past->data)->y1,
728             0, false);
730     // remove the just-added zoom from the past zooms list
731     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
734 /**
735  * Set zoom to next in list.
736  */
737 void
738 SPDesktop::next_zoom()
740     if (zooms_future == NULL) {
741         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
742         return;
743     }
745     // push current zoom into past zooms list
746     push_current_zoom (&zooms_past);
748     // restore next zoom
749     set_display_area (((NRRect *) zooms_future->data)->x0,
750             ((NRRect *) zooms_future->data)->y0,
751             ((NRRect *) zooms_future->data)->x1,
752             ((NRRect *) zooms_future->data)->y1,
753             0, false);
755     // remove the just-used zoom from the zooms_future list
756     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
759 /**
760  * Zoom to point with absolute zoom factor.
761  */
762 void
763 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
765     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
767     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
768     // this check prevents "sliding" when trying to zoom in at maximum zoom;
769     /// \todo someone please fix calculations properly and remove this hack
770     if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
771         return;
773     NR::Rect const viewbox = canvas->getViewbox();
775     double const width2 = viewbox.dimensions()[NR::X] / zoom;
776     double const height2 = viewbox.dimensions()[NR::Y] / zoom;
778     set_display_area(cx - px * width2,
779                      cy - py * height2,
780                      cx + (1 - px) * width2,
781                      cy + (1 - py) * height2,
782                      0.0);
785 /**
786  * Zoom to center with absolute zoom factor.
787  */
788 void
789 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
791     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
794 /**
795  * Zoom to point with relative zoom factor.
796  */
797 void
798 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
800     NR::Rect const area = get_display_area();
802     if (cx < area.min()[NR::X]) {
803         cx = area.min()[NR::X];
804     }
805     if (cx > area.max()[NR::X]) {
806         cx = area.max()[NR::X];
807     }
808     if (cy < area.min()[NR::Y]) {
809         cy = area.min()[NR::Y];
810     }
811     if (cy > area.max()[NR::Y]) {
812         cy = area.max()[NR::Y];
813     }
815     gdouble const scale = expansion(_d2w) * zoom;
816     double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
817     double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
819     zoom_absolute_keep_point(cx, cy, px, py, scale);
822 /**
823  * Zoom to center with relative zoom factor.
824  */
825 void
826 SPDesktop::zoom_relative (double cx, double cy, double zoom)
828     gdouble scale = expansion(_d2w) * zoom;
829     zoom_absolute (cx, cy, scale);
832 /**
833  * Set display area to origin and current document dimensions.
834  */
835 void
836 SPDesktop::zoom_page()
838     NR::Rect d(NR::Point(0, 0),
839                NR::Point(sp_document_width(doc()), sp_document_height(doc())));
841     if (d.isEmpty(1.0)) {
842         return;
843     }
845     set_display_area(d, 10);
848 /**
849  * Set display area to current document width.
850  */
851 void
852 SPDesktop::zoom_page_width()
854     NR::Rect const a = get_display_area();
856     if (sp_document_width(doc()) < 1.0) {
857         return;
858     }
860     NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
861                NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
863     set_display_area(d, 10);
866 /**
867  * Zoom to selection.
868  */
869 void
870 SPDesktop::zoom_selection()
872     NR::Maybe<NR::Rect> const d = selection->bounds();
874     if ( !d || d->isEmpty(0.1) ) {
875         return;
876     }
878     set_display_area(*d, 10);
881 /**
882  * Tell widget to let zoom widget grab keyboard focus.
883  */
884 void
885 SPDesktop::zoom_grab_focus()
887     _widget->letZoomGrabFocus();
890 /**
891  * Zoom to whole drawing.
892  */
893 void
894 SPDesktop::zoom_drawing()
896     g_return_if_fail (doc() != NULL);
897     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
898     g_return_if_fail (docitem != NULL);
900     NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
902     /* Note that the second condition here indicates that
903     ** there are no items in the drawing.
904     */
905     if ( !d || d->isEmpty(1.0) ) {
906         return;
907     }
909     set_display_area(*d, 10);
912 /**
913  * Scroll canvas by specific coordinate amount.
914  */
915 void
916 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
918     g_assert(_widget);
920     NR::Rect const viewbox = canvas->getViewbox();
922     sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
924     _widget->updateRulers();
925     _widget->updateScrollbars(expansion(_d2w));
928 bool
929 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
931     gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
933     // autoscrolldistance is in screen pixels, but the display area is in document units
934     autoscrolldistance /= expansion(_d2w);
935     NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
937     if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
938         !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y])   ) {
940         NR::Point const s_w( (*p) * _d2w );
942         gdouble x_to;
943         if ((*p)[NR::X] < dbox.min()[NR::X])
944             x_to = dbox.min()[NR::X];
945         else if ((*p)[NR::X] > dbox.max()[NR::X])
946             x_to = dbox.max()[NR::X];
947         else
948             x_to = (*p)[NR::X];
950         gdouble y_to;
951         if ((*p)[NR::Y] < dbox.min()[NR::Y])
952             y_to = dbox.min()[NR::Y];
953         else if ((*p)[NR::Y] > dbox.max()[NR::Y])
954             y_to = dbox.max()[NR::Y];
955         else
956             y_to = (*p)[NR::Y];
958         NR::Point const d_dt(x_to, y_to);
959         NR::Point const d_w( d_dt * _d2w );
960         NR::Point const moved_w( d_w - s_w );
962         if (autoscrollspeed == 0)
963             autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
965         if (autoscrollspeed != 0)
966             scroll_world (autoscrollspeed * moved_w);
968         return true;
969     }
970     return false;
973 bool
974 SPDesktop::is_iconified()
976     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
979 void
980 SPDesktop::iconify()
982     _widget->setIconified();
985 bool
986 SPDesktop::is_maximized()
988     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
991 void
992 SPDesktop::maximize()
994     _widget->setMaximized();
997 bool
998 SPDesktop::is_fullscreen()
1000     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1003 void
1004 SPDesktop::fullscreen()
1006     _widget->setFullscreen();
1009 void
1010 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1012     _widget->getGeometry (x, y, w, h);
1015 void
1016 SPDesktop::setWindowPosition (NR::Point p)
1018     _widget->setPosition (p);
1021 void
1022 SPDesktop::setWindowSize (gint w, gint h)
1024     _widget->setSize (w, h);
1027 void
1028 SPDesktop::setWindowTransient (void *p, int transient_policy)
1030     _widget->setTransient (p, transient_policy);
1033 Gtk::Window*
1034 SPDesktop::getToplevel( )
1036     return _widget->getWindow();
1039 void
1040 SPDesktop::presentWindow()
1042     _widget->present();
1045 bool
1046 SPDesktop::warnDialog (gchar *text)
1048     return _widget->warnDialog (text);
1051 void
1052 SPDesktop::toggleRulers()
1054     _widget->toggleRulers();
1057 void
1058 SPDesktop::toggleScrollbars()
1060     _widget->toggleScrollbars();
1063 void
1064 SPDesktop::layoutWidget()
1066     _widget->layout();
1069 void
1070 SPDesktop::destroyWidget()
1072     _widget->destroy();
1075 bool
1076 SPDesktop::shutdown()
1078     return _widget->shutdown();
1081 bool SPDesktop::onDeleteUI (GdkEventAny*)
1083         if(shutdown()) return true;
1084         destroyWidget();
1085         return false;
1088 /**
1089  *  onWindowStateEvent
1090  *
1091  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1092  *  Since GTK doesn't have a way to query this state information directly, we
1093  *  record it for the desktop here, and also possibly trigger a layout.
1094  */
1095 bool
1096 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1098         // Record the desktop window's state
1099     window_state = event->new_window_state;
1101     // Layout may differ depending on full-screen mode or not
1102     GdkWindowState changed = event->changed_mask;
1103     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1104         layoutWidget();
1105     }
1106         
1107         return false;
1110 void
1111 SPDesktop::setToolboxFocusTo (gchar const *label)
1113     _widget->setToolboxFocusTo (label);
1116 void
1117 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1119     _widget->setToolboxAdjustmentValue (id, val);
1122 void
1123 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1125     _widget->setToolboxSelectOneValue (id, val);
1128 bool
1129 SPDesktop::isToolboxButtonActive (gchar const *id)
1131     return _widget->isToolboxButtonActive (id);
1134 void
1135 SPDesktop::emitToolSubselectionChanged(gpointer data)
1137         _tool_subselection_changed.emit(data);
1138         inkscape_subselection_changed (this);
1141 void
1142 SPDesktop::updateNow()
1144   sp_canvas_update_now(canvas);
1147 void
1148 SPDesktop::enableInteraction()
1150   _widget->enableInteraction();
1153 void SPDesktop::disableInteraction()
1155   _widget->disableInteraction();
1158 void SPDesktop::setWaitingCursor()
1160     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1161     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1162     gdk_cursor_unref(waiting);
1163     waiting_cursor = true;
1165     // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1166     // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1167     // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1168     // after the call to setWaitingCursor as it was before
1169     while( Gtk::Main::events_pending() )
1170        Gtk::Main::iteration();
1173 void SPDesktop::clearWaitingCursor()
1175   if (waiting_cursor)
1176       sp_event_context_update_cursor(sp_desktop_event_context(this));
1179 void SPDesktop::toggleGrids()
1181     if (namedview->grids) {
1182         if(gridgroup) {
1183             showGrids(!grids_visible);
1184         }
1185     } else {
1186         //there is no grid present at the moment. add a rectangular grid and make it visible
1187         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1188         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1189         showGrids(true);
1190     }
1193 void SPDesktop::showGrids(bool show)
1195     grids_visible = show;
1196     sp_namedview_show_grids(namedview, grids_visible);
1197     if (show) {
1198         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1199     } else {
1200         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1201     }
1205 //----------------------------------------------------------------------
1206 // Callback implementations. The virtual ones are connected by the view.
1208 void
1209 SPDesktop::onPositionSet (double x, double y)
1211     _widget->viewSetPosition (NR::Point(x,y));
1214 void
1215 SPDesktop::onResized (double /*x*/, double /*y*/)
1217    // Nothing called here
1220 /**
1221  * Redraw callback; queues Gtk redraw; connected by View.
1222  */
1223 void
1224 SPDesktop::onRedrawRequested ()
1226     if (main) {
1227         _widget->requestCanvasUpdate();
1228     }
1231 void
1232 SPDesktop::updateCanvasNow()
1234   _widget->requestCanvasUpdateAndWait();
1237 /**
1238  * Associate document with desktop.
1239  */
1240 void
1241 SPDesktop::setDocument (SPDocument *doc)
1243     if (this->doc() && doc) {
1244         namedview->hide(this);
1245         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1246     }
1248     if (_layer_hierarchy) {
1249         _layer_hierarchy->clear();
1250         delete _layer_hierarchy;
1251     }
1252     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1253     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1254     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1255     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1256     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1258     /* setup EventLog */
1259     event_log = new Inkscape::EventLog(doc);
1260     doc->addUndoObserver(*event_log);
1262     _commit_connection.disconnect();
1263     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1265     /// \todo fixme: This condition exists to make sure the code
1266     /// inside is NOT called on initialization, only on replacement. But there
1267     /// are surely more safe methods to accomplish this.
1268     // TODO since the comment had reversed logic, check the intent of this block of code:
1269     if (drawing) {
1270         NRArenaItem *ai = 0;
1272         namedview = sp_document_namedview (doc, NULL);
1273         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1274         number = namedview->getViewCount();
1276         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1277                 SP_CANVAS_ARENA (drawing)->arena,
1278                 dkey,
1279                 SP_ITEM_SHOW_DISPLAY);
1280         if (ai) {
1281             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1282             nr_arena_item_unref (ai);
1283         }
1284         namedview->show(this);
1285         /* Ugly hack */
1286         activate_guides (true);
1287         /* Ugly hack */
1288         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1289     }
1291     _document_replaced_signal.emit (this, doc);
1293     View::setDocument (doc);
1296 void
1297 SPDesktop::onStatusMessage
1298 (Inkscape::MessageType type, gchar const *message)
1300     if (_widget) {
1301         _widget->setMessage(type, message);
1302     }
1305 void
1306 SPDesktop::onDocumentURISet (gchar const* uri)
1308     _widget->setTitle(uri);
1311 /**
1312  * Resized callback.
1313  */
1314 void
1315 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1317     _doc2dt[5] = height;
1318     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1319     NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1320     SP_CTRLRECT(page)->setRectangle(a);
1321     SP_CTRLRECT(page_border)->setRectangle(a);
1325 void
1326 SPDesktop::_onActivate (SPDesktop* dt)
1328     if (!dt->_widget) return;
1329     dt->_widget->activateDesktop();
1332 void
1333 SPDesktop::_onDeactivate (SPDesktop* dt)
1335     if (!dt->_widget) return;
1336     dt->_widget->deactivateDesktop();
1339 void
1340 SPDesktop::_onSelectionModified
1341 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1343     if (!dt->_widget) return;
1344     dt->_widget->updateScrollbars (expansion(dt->_d2w));
1347 static void
1348 _onSelectionChanged
1349 (Inkscape::Selection *selection, SPDesktop *desktop)
1351     /** \todo
1352      * only change the layer for single selections, or what?
1353      * This seems reasonable -- for multiple selections there can be many
1354      * different layers involved.
1355      */
1356     SPItem *item=selection->singleItem();
1357     if (item) {
1358         SPObject *layer=desktop->layerForObject(item);
1359         if ( layer && layer != desktop->currentLayer() ) {
1360             desktop->setCurrentLayer(layer);
1361         }
1362     }
1365 /**
1366  * Calls event handler of current event context.
1367  * \param arena Unused
1368  * \todo fixme
1369  */
1370 static gint
1371 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1373     if (ai) {
1374         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1375         return sp_event_context_item_handler (desktop->event_context, spi, event);
1376     } else {
1377         return sp_event_context_root_handler (desktop->event_context, event);
1378     }
1381 static void
1382 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1383     g_return_if_fail(SP_IS_GROUP(layer));
1384     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1387 /// Callback
1388 static void
1389 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1390     g_return_if_fail(SP_IS_GROUP(layer));
1391     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1394 /// Callback
1395 static void
1396 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1397                                          SPDesktop *desktop)
1399     desktop->_layer_changed_signal.emit (bottom);
1402 /// Called when document is starting to be rebuilt.
1403 static void
1404 _reconstruction_start (SPDesktop * desktop)
1406     // printf("Desktop, starting reconstruction\n");
1407     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1408     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1410     /*
1411     GSList const * selection_objs = desktop->selection->list();
1412     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1414     }
1415     */
1416     desktop->selection->clear();
1418     // printf("Desktop, starting reconstruction end\n");
1421 /// Called when document rebuild is finished.
1422 static void
1423 _reconstruction_finish (SPDesktop * desktop)
1425     // printf("Desktop, finishing reconstruction\n");
1426     if (desktop->_reconstruction_old_layer_id == NULL)
1427         return;
1429     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1430     if (newLayer != NULL)
1431         desktop->setCurrentLayer(newLayer);
1433     g_free(desktop->_reconstruction_old_layer_id);
1434     desktop->_reconstruction_old_layer_id = NULL;
1435     // printf("Desktop, finishing reconstruction end\n");
1436     return;
1439 /**
1440  * Namedview_modified callback.
1441  */
1442 static void
1443 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1445     SPNamedView *nv=SP_NAMEDVIEW(obj);
1447     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1449         /* Recalculate snap distances */
1450         /* FIXME: why is the desktop getting involved in setting up something
1451         ** that is entirely to do with the namedview?
1452         */
1453         _update_snap_distances (desktop);
1455         /* Show/hide page background */
1456         if (nv->pagecolor & 0xff) {
1457             sp_canvas_item_show (desktop->table);
1458             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1459             sp_canvas_item_move_to_z (desktop->table, 0);
1460         } else {
1461             sp_canvas_item_hide (desktop->table);
1462         }
1464         /* Show/hide page border */
1465         if (nv->showborder) {
1466             // show
1467             sp_canvas_item_show (desktop->page_border);
1468             // set color and shadow
1469             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1470             if (nv->pageshadow) {
1471                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1472             }
1473             // place in the z-order stack
1474             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1475                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1476             } else {
1477                 int order = sp_canvas_item_order (desktop->page_border);
1478                 int morder = sp_canvas_item_order (desktop->drawing);
1479                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1480                                     morder - order);
1481             }
1482         } else {
1483                 sp_canvas_item_hide (desktop->page_border);
1484                 if (nv->pageshadow) {
1485                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1486                 }
1487         }
1488         
1489         /* Show/hide page shadow */
1490         if (nv->showpageshadow && nv->pageshadow) {
1491             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1492         } else {
1493             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1494         }
1496         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1497             (SP_RGBA32_R_U(nv->pagecolor) +
1498              SP_RGBA32_G_U(nv->pagecolor) +
1499              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1500             // the background color is light or transparent, use black outline
1501             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1502         } else { // use white outline
1503             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1504         }
1505     }
1508 /**
1509  * Callback to reset snapper's distances.
1510  */
1511 static void
1512 _update_snap_distances (SPDesktop *desktop)
1514     SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1516     SPNamedView &nv = *desktop->namedview;
1518     //tell all grid snappers
1519     for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1520         Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1521         grid->snapper->setDistance(sp_convert_distance_full(nv.gridtolerance,
1522                                                                       *nv.gridtoleranceunit,
1523                                                                       px));
1524     }
1525     
1526     nv.snap_manager.guide.setDistance(sp_convert_distance_full(nv.guidetolerance,
1527                                                                        *nv.guidetoleranceunit,
1528                                                                        px));
1529     nv.snap_manager.object.setDistance(sp_convert_distance_full(nv.objecttolerance,
1530                                                                         *nv.objecttoleranceunit,
1531                                                                         px));
1535 NR::Matrix SPDesktop::w2d() const
1537     return _w2d;
1540 NR::Point SPDesktop::w2d(NR::Point const &p) const
1542     return p * _w2d;
1545 NR::Point SPDesktop::d2w(NR::Point const &p) const
1547     return p * _d2w;
1550 NR::Matrix SPDesktop::doc2dt() const
1552     return _doc2dt;
1555 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1557     return p * _doc2dt;
1560 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1562     return p / _doc2dt;
1566 /**
1567  * Pop event context from desktop's context stack. Never used.
1568  */
1569 // void
1570 // SPDesktop::pop_event_context (unsigned int key)
1571 // {
1572 //    SPEventContext *ec = NULL;
1573 //
1574 //    if (event_context && event_context->key == key) {
1575 //        g_return_if_fail (event_context);
1576 //        g_return_if_fail (event_context->next);
1577 //        ec = event_context;
1578 //        sp_event_context_deactivate (ec);
1579 //        event_context = ec->next;
1580 //        sp_event_context_activate (event_context);
1581 //        _event_context_changed_signal.emit (this, ec);
1582 //    }
1583 //
1584 //    SPEventContext *ref = event_context;
1585 //    while (ref && ref->next && ref->next->key != key)
1586 //        ref = ref->next;
1587 //
1588 //    if (ref && ref->next) {
1589 //        ec = ref->next;
1590 //        ref->next = ec->next;
1591 //    }
1592 //
1593 //    if (ec) {
1594 //        sp_event_context_finish (ec);
1595 //        g_object_unref (G_OBJECT (ec));
1596 //    }
1597 // }
1599 /*
1600   Local Variables:
1601   mode:c++
1602   c-file-style:"stroustrup"
1603   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1604   indent-tabs-mode:nil
1605   fill-column:99
1606   End:
1607 */
1608 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :