Code

NEW: temporary on-canvas indicators
[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-2008 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 "display/canvas-temporary-item-list.h"
81 #include "libnr/nr-matrix-div.h"
82 #include "libnr/nr-rect-ops.h"
83 #include "ui/dialog/dialog-manager.h"
84 #include "xml/repr.h"
85 #include "message-context.h"
86 #include "layer-manager.h"
87 #include "event-log.h"
88 #include "display/canvas-grid.h"
89 #include "widgets/desktop-widget.h"
91 #include "display/sp-canvas.h"
93 namespace Inkscape { namespace XML { class Node; }}
95 // Callback declarations
96 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
97 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
98 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
99 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
100 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
101 static void _reconstruction_start(SPDesktop * desktop);
102 static void _reconstruction_finish(SPDesktop * desktop);
103 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
104 static void _update_snap_distances (SPDesktop *desktop);
106 /**
107  * Return new desktop object.
108  * \pre namedview != NULL.
109  * \pre canvas != NULL.
110  */
111 SPDesktop::SPDesktop() :
112     _dlg_mgr( 0 ),
113     namedview( 0 ),
114     canvas( 0 ),
115     selection( 0 ),
116     event_context( 0 ),
117     layer_manager( 0 ),
118     event_log( 0 ),
119     temporary_item_list( 0 ),
120     acetate( 0 ),
121     main( 0 ),
122     gridgroup( 0 ),
123     guides( 0 ),
124     drawing( 0 ),
125     sketch( 0 ),
126     controls( 0 ),
127     tempgroup ( 0 ),
128     table( 0 ),
129     page( 0 ),
130     page_border( 0 ),
131     current( 0 ),
132     zooms_past( 0 ),
133     zooms_future( 0 ),
134     dkey( 0 ),
135     number( 0 ),
136     window_state(0),
137     interaction_disabled_counter( 0 ),
138     waiting_cursor( false ),
139     guides_active( false ),
140     gr_item( 0 ),
141     gr_point_type( 0 ),
142     gr_point_i( 0 ),
143     gr_fill_or_stroke( true ),
144     _layer_hierarchy( 0 ),
145     _reconstruction_old_layer_id( 0 ),
146     _widget( 0 ),
147     _inkscape( 0 ),
148     _guides_message_context( 0 ),
149     _active( false ),
150     _w2d(),
151     _d2w(),
152     _doc2dt( NR::Matrix(NR::scale(1, -1)) ),
153     grids_visible( false )
155     _d2w.set_identity();
156     _w2d.set_identity();
158     selection = Inkscape::GC::release( new Inkscape::Selection(this) );
161 void
162 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
164     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
166     current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
168     namedview = nv;
169     canvas = aCanvas;
171     SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
172     /* Kill flicker */
173     sp_document_ensure_up_to_date (document);
175     /* Setup Dialog Manager */
176     _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
178     dkey = sp_item_display_key_new (1);
180     /* Connect document */
181     setDocument (document);
183     number = namedview->getViewCount();
186     /* Setup Canvas */
187     g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
189     SPCanvasGroup *root = sp_canvas_root (canvas);
191     /* Setup adminstrative layers */
192     acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
193     g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
194     main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
195     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
197     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
198     SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
199     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
200     sp_canvas_item_move_to_z (table, 0);
202     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
203     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
204     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
206     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
207     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
209     SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
211     if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
212         // Start in outline mode
213         setDisplayModeOutline();
214     } else {
215         // Start in normal mode, default
216         setDisplayModeNormal();
217     }
219     gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
220     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
221     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
222     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
223     tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
225     /* Push select tool to the bottom of stack */
226     /** \todo
227      * FIXME: this is the only call to this.  Everything else seems to just
228      * call "set" instead of "push".  Can we assume that there is only one
229      * context ever?
230      */
231     push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
233     // display rect and zoom are now handled in sp_desktop_widget_realize()
235     NR::Rect const d(NR::Point(0.0, 0.0),
236                      NR::Point(sp_document_width(document), sp_document_height(document)));
238     SP_CTRLRECT(page)->setRectangle(d);
239     SP_CTRLRECT(page_border)->setRectangle(d);
241     /* the following sets the page shadow on the canvas
242        It was originally set to 5, which is really cheesy!
243        It now is an attribute in the document's namedview. If a value of
244        0 is used, then the constructor for a shadow is not initialized.
245     */
247     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
248         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
249     }
252     /* Connect event for page resize */
253     _doc2dt[5] = sp_document_height (document);
254     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
256     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
258     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
259             SP_CANVAS_ARENA (drawing)->arena,
260             dkey,
261             SP_ITEM_SHOW_DISPLAY);
262     if (ai) {
263         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
264         nr_arena_item_unref (ai);
265     }
267     namedview->show(this);
268     /* Ugly hack */
269     activate_guides (true);
270     /* Ugly hack */
271     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
273 /* Set up notification of rebuilding the document, this allows
274        for saving object related settings in the document. */
275     _reconstruction_start_connection =
276         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
277     _reconstruction_finish_connection =
278         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
279     _reconstruction_old_layer_id = NULL;
281     // ?
282     // sp_active_desktop_set (desktop);
283     _inkscape = INKSCAPE;
285     _activate_connection = _activate_signal.connect(
286         sigc::bind(
287             sigc::ptr_fun(_onActivate),
288             this
289         )
290     );
291      _deactivate_connection = _deactivate_signal.connect(
292         sigc::bind(
293             sigc::ptr_fun(_onDeactivate),
294             this
295         )
296     );
298     _sel_modified_connection = selection->connectModified(
299         sigc::bind(
300             sigc::ptr_fun(&_onSelectionModified),
301             this
302         )
303     );
304     _sel_changed_connection = selection->connectChanged(
305         sigc::bind(
306             sigc::ptr_fun(&_onSelectionChanged),
307             this
308         )
309     );
312     /* setup LayerManager */
313     //   (Setting up after the connections are all in place, as it may use some of them)
314     layer_manager = new Inkscape::LayerManager( this );
316     showGrids(namedview->grids_visible, false);
318     temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
322 void SPDesktop::destroy()
324     delete temporary_item_list;
325     temporary_item_list = NULL;
327     namedview->hide(this);
329     _activate_connection.disconnect();
330     _deactivate_connection.disconnect();
331     _sel_modified_connection.disconnect();
332     _sel_changed_connection.disconnect();
333     _modified_connection.disconnect();
334     _commit_connection.disconnect();
335     _reconstruction_start_connection.disconnect();
336     _reconstruction_finish_connection.disconnect();
338     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
339     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
340     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
342     while (event_context) {
343         SPEventContext *ec = event_context;
344         event_context = ec->next;
345         sp_event_context_finish (ec);
346         g_object_unref (G_OBJECT (ec));
347     }
349     if (_layer_hierarchy) {
350         delete _layer_hierarchy;
351 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
352     }
354     if (layer_manager) {
355         delete layer_manager;
356         layer_manager = NULL;
357     }
359     if (_inkscape) {
360         _inkscape = NULL;
361     }
363     if (drawing) {
364         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
365         drawing = NULL;
366     }
368     delete _guides_message_context;
369     _guides_message_context = NULL;
371     g_list_free (zooms_past);
372     g_list_free (zooms_future);
375 SPDesktop::~SPDesktop() {}
377 //--------------------------------------------------------------------
378 /* Public methods */
381 /** Note that lifetime is measured in milliseconds
382 * it is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
383 * The return value should only be used as argument for SPDesktop::remove_temporary_canvasitem, because the object might be deleted already.
384 */
385 Inkscape::Display::TemporaryItem *
386 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime)
388     return temporary_item_list->add_item(item, lifetime);
391 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
392 */
393 void
394 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
396     temporary_item_list->delete_item(tempitem);
399 void SPDesktop::setDisplayModeNormal()
401     SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
402     canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
403     displayMode = RENDERMODE_NORMAL;
404     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
405     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
408 void SPDesktop::setDisplayModeOutline()
410     SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
411     canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
412     displayMode = RENDERMODE_OUTLINE;
413     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
414     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
417 void SPDesktop::displayModeToggle()
419     if (displayMode == RENDERMODE_OUTLINE)
420         setDisplayModeNormal();
421     else
422         setDisplayModeOutline();
425 /**
426  * Returns current root (=bottom) layer.
427  */
428 SPObject *SPDesktop::currentRoot() const
430     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
433 /**
434  * Returns current top layer.
435  */
436 SPObject *SPDesktop::currentLayer() const
438     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
441 /**
442  * Sets the current layer of the desktop.
443  *
444  * Make \a object the top layer.
445  */
446 void SPDesktop::setCurrentLayer(SPObject *object) {
447     g_return_if_fail(SP_IS_GROUP(object));
448     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
449     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
450     _layer_hierarchy->setBottom(object);
453 /**
454  * Return layer that contains \a object.
455  */
456 SPObject *SPDesktop::layerForObject(SPObject *object) {
457     g_return_val_if_fail(object != NULL, NULL);
459     SPObject *root=currentRoot();
460     object = SP_OBJECT_PARENT(object);
461     while ( object && object != root && !isLayer(object) ) {
462         object = SP_OBJECT_PARENT(object);
463     }
464     return object;
467 /**
468  * True if object is a layer.
469  */
470 bool SPDesktop::isLayer(SPObject *object) const {
471     return ( SP_IS_GROUP(object)
472              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
473                   == SPGroup::LAYER ) );
476 /**
477  * True if desktop viewport fully contains \a item's bbox.
478  */
479 bool SPDesktop::isWithinViewport (SPItem *item) const
481     NR::Rect const viewport = get_display_area();
482     NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
483     if (bbox) {
484         return viewport.contains(*bbox);
485     } else {
486         return true;
487     }
490 ///
491 bool SPDesktop::itemIsHidden(SPItem const *item) const {
492     return item->isHidden(this->dkey);
495 /**
496  * Set activate property of desktop; emit signal if changed.
497  */
498 void
499 SPDesktop::set_active (bool new_active)
501     if (new_active != _active) {
502         _active = new_active;
503         if (new_active) {
504             _activate_signal.emit();
505         } else {
506             _deactivate_signal.emit();
507         }
508     }
511 /**
512  * Set activate status of current desktop's named view.
513  */
514 void
515 SPDesktop::activate_guides(bool activate)
517     guides_active = activate;
518     namedview->activateGuides(this, activate);
521 /**
522  * Make desktop switch documents.
523  */
524 void
525 SPDesktop::change_document (SPDocument *theDocument)
527     g_return_if_fail (theDocument != NULL);
529     /* unselect everything before switching documents */
530     selection->clear();
532     setDocument (theDocument);
534     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
535        (this can probably be done in a better way) */
536     Gtk::Window *parent = this->getToplevel();
537     g_assert(parent != NULL);
538     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
539     if (dtw) dtw->desktop = this;
540     sp_desktop_widget_update_namedview(dtw);
542     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
543     _document_replaced_signal.emit (this, theDocument);
546 /**
547  * Make desktop switch event contexts.
548  */
549 void
550 SPDesktop::set_event_context (GtkType type, const gchar *config)
552     SPEventContext *ec;
553     while (event_context) {
554         ec = event_context;
555         sp_event_context_deactivate (ec);
556         event_context = ec->next;
557         sp_event_context_finish (ec);
558         g_object_unref (G_OBJECT (ec));
559     }
561     Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
562     ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
563     ec->next = event_context;
564     event_context = ec;
565     sp_event_context_activate (ec);
566     _event_context_changed_signal.emit (this, ec);
569 /**
570  * Push event context onto desktop's context stack.
571  */
572 void
573 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
575     SPEventContext *ref, *ec;
576     Inkscape::XML::Node *repr;
578     if (event_context && event_context->key == key) return;
579     ref = event_context;
580     while (ref && ref->next && ref->next->key != key) ref = ref->next;
581     if (ref && ref->next) {
582         ec = ref->next;
583         ref->next = ec->next;
584         sp_event_context_finish (ec);
585         g_object_unref (G_OBJECT (ec));
586     }
588     if (event_context) sp_event_context_deactivate (event_context);
589     repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
590     ec = sp_event_context_new (type, this, repr, key);
591     ec->next = event_context;
592     event_context = ec;
593     sp_event_context_activate (ec);
594     _event_context_changed_signal.emit (this, ec);
597 /**
598  * Sets the coordinate status to a given point
599  */
600 void
601 SPDesktop::set_coordinate_status (NR::Point p) {
602     _widget->setCoordinateStatus(p);
605 /**
606  * \see sp_document_item_from_list_at_point_bottom()
607  */
608 SPItem *
609 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
611     g_return_val_if_fail (doc() != NULL, NULL);
612     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
615 /**
616  * \see sp_document_item_at_point()
617  */
618 SPItem *
619 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
621     g_return_val_if_fail (doc() != NULL, NULL);
622     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
625 /**
626  * \see sp_document_group_at_point()
627  */
628 SPItem *
629 SPDesktop::group_at_point (NR::Point const p) const
631     g_return_val_if_fail (doc() != NULL, NULL);
632     return sp_document_group_at_point (doc(), dkey, p);
635 /**
636  * \brief  Returns the mouse point in document coordinates; if mouse is
637  * outside the canvas, returns the center of canvas viewpoint
638  */
639 NR::Point
640 SPDesktop::point() const
642     NR::Point p = _widget->getPointer();
643     NR::Point pw = sp_canvas_window_to_world (canvas, p);
644     p = w2d(pw);
646     NR::Rect const r = canvas->getViewbox();
648     NR::Point r0 = w2d(r.min());
649     NR::Point r1 = w2d(r.max());
651     if (p[NR::X] >= r0[NR::X] &&
652         p[NR::X] <= r1[NR::X] &&
653         p[NR::Y] >= r1[NR::Y] &&
654         p[NR::Y] <= r0[NR::Y])
655     {
656         return p;
657     } else {
658         return (r0 + r1) / 2;
659     }
662 /**
663  * Put current zoom data in history list.
664  */
665 void
666 SPDesktop::push_current_zoom (GList **history)
668     NR::Rect const area = get_display_area();
670     NRRect *old_zoom = g_new(NRRect, 1);
671     old_zoom->x0 = area.min()[NR::X];
672     old_zoom->x1 = area.max()[NR::X];
673     old_zoom->y0 = area.min()[NR::Y];
674     old_zoom->y1 = area.max()[NR::Y];
675     if ( *history == NULL
676          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
677                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
678                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
679                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
680     {
681         *history = g_list_prepend (*history, old_zoom);
682     }
685 /**
686  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
687  */
688 void
689 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
691     g_assert(_widget);
693     // save the zoom
694     if (log) {
695         push_current_zoom(&zooms_past);
696         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
697         g_list_free (zooms_future);
698         zooms_future = NULL;
699     }
701     double const cx = 0.5 * (x0 + x1);
702     double const cy = 0.5 * (y0 + y1);
704     NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
706     double scale = expansion(_d2w);
707     double newscale;
708     if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
709         newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
710     } else {
711         newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
712     }
714     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
716     int clear = FALSE;
717     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
718         /* Set zoom factors */
719         _d2w = NR::Matrix(NR::scale(newscale, -newscale));
720         _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
721         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
722         clear = TRUE;
723     }
725     /* Calculate top left corner (in document pixels) */
726     x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
727     y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
729     /* Scroll */
730     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
732     _widget->updateRulers();
733     _widget->updateScrollbars(expansion(_d2w));
734     _widget->updateZoom();
737 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
739     set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
742 /**
743  * Return viewbox dimensions.
744  */
745 NR::Rect SPDesktop::get_display_area() const
747     NR::Rect const viewbox = canvas->getViewbox();
749     double const scale = _d2w[0];
751     return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
752                     NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
755 /**
756  * Revert back to previous zoom if possible.
757  */
758 void
759 SPDesktop::prev_zoom()
761     if (zooms_past == NULL) {
762         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
763         return;
764     }
766     // push current zoom into forward zooms list
767     push_current_zoom (&zooms_future);
769     // restore previous zoom
770     set_display_area (((NRRect *) zooms_past->data)->x0,
771             ((NRRect *) zooms_past->data)->y0,
772             ((NRRect *) zooms_past->data)->x1,
773             ((NRRect *) zooms_past->data)->y1,
774             0, false);
776     // remove the just-added zoom from the past zooms list
777     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
780 /**
781  * Set zoom to next in list.
782  */
783 void
784 SPDesktop::next_zoom()
786     if (zooms_future == NULL) {
787         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
788         return;
789     }
791     // push current zoom into past zooms list
792     push_current_zoom (&zooms_past);
794     // restore next zoom
795     set_display_area (((NRRect *) zooms_future->data)->x0,
796             ((NRRect *) zooms_future->data)->y0,
797             ((NRRect *) zooms_future->data)->x1,
798             ((NRRect *) zooms_future->data)->y1,
799             0, false);
801     // remove the just-used zoom from the zooms_future list
802     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
805 /**
806  * Zoom to point with absolute zoom factor.
807  */
808 void
809 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
811     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
813     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
814     // this check prevents "sliding" when trying to zoom in at maximum zoom;
815     /// \todo someone please fix calculations properly and remove this hack
816     if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
817         return;
819     NR::Rect const viewbox = canvas->getViewbox();
821     double const width2 = viewbox.dimensions()[NR::X] / zoom;
822     double const height2 = viewbox.dimensions()[NR::Y] / zoom;
824     set_display_area(cx - px * width2,
825                      cy - py * height2,
826                      cx + (1 - px) * width2,
827                      cy + (1 - py) * height2,
828                      0.0);
831 /**
832  * Zoom to center with absolute zoom factor.
833  */
834 void
835 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
837     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
840 /**
841  * Zoom to point with relative zoom factor.
842  */
843 void
844 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
846     NR::Rect const area = get_display_area();
848     if (cx < area.min()[NR::X]) {
849         cx = area.min()[NR::X];
850     }
851     if (cx > area.max()[NR::X]) {
852         cx = area.max()[NR::X];
853     }
854     if (cy < area.min()[NR::Y]) {
855         cy = area.min()[NR::Y];
856     }
857     if (cy > area.max()[NR::Y]) {
858         cy = area.max()[NR::Y];
859     }
861     gdouble const scale = expansion(_d2w) * zoom;
862     double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
863     double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
865     zoom_absolute_keep_point(cx, cy, px, py, scale);
868 /**
869  * Zoom to center with relative zoom factor.
870  */
871 void
872 SPDesktop::zoom_relative (double cx, double cy, double zoom)
874     gdouble scale = expansion(_d2w) * zoom;
875     zoom_absolute (cx, cy, scale);
878 /**
879  * Set display area to origin and current document dimensions.
880  */
881 void
882 SPDesktop::zoom_page()
884     NR::Rect d(NR::Point(0, 0),
885                NR::Point(sp_document_width(doc()), sp_document_height(doc())));
887     if (d.isEmpty(1.0)) {
888         return;
889     }
891     set_display_area(d, 10);
894 /**
895  * Set display area to current document width.
896  */
897 void
898 SPDesktop::zoom_page_width()
900     NR::Rect const a = get_display_area();
902     if (sp_document_width(doc()) < 1.0) {
903         return;
904     }
906     NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
907                NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
909     set_display_area(d, 10);
912 /**
913  * Zoom to selection.
914  */
915 void
916 SPDesktop::zoom_selection()
918     NR::Maybe<NR::Rect> const d = selection->bounds();
920     if ( !d || d->isEmpty(0.1) ) {
921         return;
922     }
924     set_display_area(*d, 10);
927 /**
928  * Tell widget to let zoom widget grab keyboard focus.
929  */
930 void
931 SPDesktop::zoom_grab_focus()
933     _widget->letZoomGrabFocus();
936 /**
937  * Zoom to whole drawing.
938  */
939 void
940 SPDesktop::zoom_drawing()
942     g_return_if_fail (doc() != NULL);
943     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
944     g_return_if_fail (docitem != NULL);
946     NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
948     /* Note that the second condition here indicates that
949     ** there are no items in the drawing.
950     */
951     if ( !d || d->isEmpty(1.0) ) {
952         return;
953     }
955     set_display_area(*d, 10);
958 /**
959  * Scroll canvas by specific coordinate amount.
960  */
961 void
962 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
964     g_assert(_widget);
966     NR::Rect const viewbox = canvas->getViewbox();
968     sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
970     _widget->updateRulers();
971     _widget->updateScrollbars(expansion(_d2w));
974 bool
975 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
977     gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
979     // autoscrolldistance is in screen pixels, but the display area is in document units
980     autoscrolldistance /= expansion(_d2w);
981     NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
983     if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
984         !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y])   ) {
986         NR::Point const s_w( (*p) * _d2w );
988         gdouble x_to;
989         if ((*p)[NR::X] < dbox.min()[NR::X])
990             x_to = dbox.min()[NR::X];
991         else if ((*p)[NR::X] > dbox.max()[NR::X])
992             x_to = dbox.max()[NR::X];
993         else
994             x_to = (*p)[NR::X];
996         gdouble y_to;
997         if ((*p)[NR::Y] < dbox.min()[NR::Y])
998             y_to = dbox.min()[NR::Y];
999         else if ((*p)[NR::Y] > dbox.max()[NR::Y])
1000             y_to = dbox.max()[NR::Y];
1001         else
1002             y_to = (*p)[NR::Y];
1004         NR::Point const d_dt(x_to, y_to);
1005         NR::Point const d_w( d_dt * _d2w );
1006         NR::Point const moved_w( d_w - s_w );
1008         if (autoscrollspeed == 0)
1009             autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
1011         if (autoscrollspeed != 0)
1012             scroll_world (autoscrollspeed * moved_w);
1014         return true;
1015     }
1016     return false;
1019 bool
1020 SPDesktop::is_iconified()
1022     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1025 void
1026 SPDesktop::iconify()
1028     _widget->setIconified();
1031 bool
1032 SPDesktop::is_maximized()
1034     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1037 void
1038 SPDesktop::maximize()
1040     _widget->setMaximized();
1043 bool
1044 SPDesktop::is_fullscreen()
1046     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1049 void
1050 SPDesktop::fullscreen()
1052     _widget->setFullscreen();
1055 void
1056 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1058     _widget->getGeometry (x, y, w, h);
1061 void
1062 SPDesktop::setWindowPosition (NR::Point p)
1064     _widget->setPosition (p);
1067 void
1068 SPDesktop::setWindowSize (gint w, gint h)
1070     _widget->setSize (w, h);
1073 void
1074 SPDesktop::setWindowTransient (void *p, int transient_policy)
1076     _widget->setTransient (p, transient_policy);
1079 Gtk::Window*
1080 SPDesktop::getToplevel( )
1082     return _widget->getWindow();
1085 void
1086 SPDesktop::presentWindow()
1088     _widget->present();
1091 bool
1092 SPDesktop::warnDialog (gchar *text)
1094     return _widget->warnDialog (text);
1097 void
1098 SPDesktop::toggleRulers()
1100     _widget->toggleRulers();
1103 void
1104 SPDesktop::toggleScrollbars()
1106     _widget->toggleScrollbars();
1109 void
1110 SPDesktop::layoutWidget()
1112     _widget->layout();
1115 void
1116 SPDesktop::destroyWidget()
1118     _widget->destroy();
1121 bool
1122 SPDesktop::shutdown()
1124     return _widget->shutdown();
1127 bool SPDesktop::onDeleteUI (GdkEventAny*)
1129     if(shutdown()) 
1130         return true;
1132     destroyWidget();
1133     return false;
1136 /**
1137  *  onWindowStateEvent
1138  *
1139  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1140  *  Since GTK doesn't have a way to query this state information directly, we
1141  *  record it for the desktop here, and also possibly trigger a layout.
1142  */
1143 bool
1144 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1146         // Record the desktop window's state
1147     window_state = event->new_window_state;
1149     // Layout may differ depending on full-screen mode or not
1150     GdkWindowState changed = event->changed_mask;
1151     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1152         layoutWidget();
1153     }
1155         return false;
1158 void
1159 SPDesktop::setToolboxFocusTo (gchar const *label)
1161     _widget->setToolboxFocusTo (label);
1164 void
1165 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1167     _widget->setToolboxAdjustmentValue (id, val);
1170 void
1171 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1173     _widget->setToolboxSelectOneValue (id, val);
1176 bool
1177 SPDesktop::isToolboxButtonActive (gchar const *id)
1179     return _widget->isToolboxButtonActive (id);
1182 void
1183 SPDesktop::emitToolSubselectionChanged(gpointer data)
1185         _tool_subselection_changed.emit(data);
1186         inkscape_subselection_changed (this);
1189 void
1190 SPDesktop::updateNow()
1192   sp_canvas_update_now(canvas);
1195 void
1196 SPDesktop::enableInteraction()
1198   _widget->enableInteraction();
1201 void SPDesktop::disableInteraction()
1203   _widget->disableInteraction();
1206 void SPDesktop::setWaitingCursor()
1208     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1209     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1210     gdk_cursor_unref(waiting);
1211     waiting_cursor = true;
1213     // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1214     // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1215     // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1216     // after the call to setWaitingCursor as it was before
1217     while( Gtk::Main::events_pending() )
1218        Gtk::Main::iteration();
1221 void SPDesktop::clearWaitingCursor()
1223   if (waiting_cursor)
1224       sp_event_context_update_cursor(sp_desktop_event_context(this));
1227 void SPDesktop::toggleColorProfAdjust()
1229     _widget->toggleColorProfAdjust();
1232 void SPDesktop::toggleGrids()
1234     if (namedview->grids) {
1235         if(gridgroup) {
1236             showGrids(!grids_visible);
1237         }
1238     } else {
1239         //there is no grid present at the moment. add a rectangular grid and make it visible
1240         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1241         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1242         showGrids(true);
1243     }
1246 void SPDesktop::showGrids(bool show, bool dirty_document)
1248     grids_visible = show;
1249     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1250     if (show) {
1251         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1252     } else {
1253         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1254     }
1257 void SPDesktop::toggleSnapping()
1259     bool v = namedview->snap_manager.getSnapEnabledGlobally();
1260     Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1261     sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1264 //----------------------------------------------------------------------
1265 // Callback implementations. The virtual ones are connected by the view.
1267 void
1268 SPDesktop::onPositionSet (double x, double y)
1270     _widget->viewSetPosition (NR::Point(x,y));
1273 void
1274 SPDesktop::onResized (double /*x*/, double /*y*/)
1276    // Nothing called here
1279 /**
1280  * Redraw callback; queues Gtk redraw; connected by View.
1281  */
1282 void
1283 SPDesktop::onRedrawRequested ()
1285     if (main) {
1286         _widget->requestCanvasUpdate();
1287     }
1290 void
1291 SPDesktop::updateCanvasNow()
1293   _widget->requestCanvasUpdateAndWait();
1296 /**
1297  * Associate document with desktop.
1298  */
1299 void
1300 SPDesktop::setDocument (SPDocument *doc)
1302     if (this->doc() && doc) {
1303         namedview->hide(this);
1304         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1305     }
1307     if (_layer_hierarchy) {
1308         _layer_hierarchy->clear();
1309         delete _layer_hierarchy;
1310     }
1311     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1312     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1313     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1314     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1315     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1317     /* setup EventLog */
1318     event_log = new Inkscape::EventLog(doc);
1319     doc->addUndoObserver(*event_log);
1321     _commit_connection.disconnect();
1322     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1324     /// \todo fixme: This condition exists to make sure the code
1325     /// inside is NOT called on initialization, only on replacement. But there
1326     /// are surely more safe methods to accomplish this.
1327     // TODO since the comment had reversed logic, check the intent of this block of code:
1328     if (drawing) {
1329         NRArenaItem *ai = 0;
1331         namedview = sp_document_namedview (doc, NULL);
1332         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1333         number = namedview->getViewCount();
1335         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1336                 SP_CANVAS_ARENA (drawing)->arena,
1337                 dkey,
1338                 SP_ITEM_SHOW_DISPLAY);
1339         if (ai) {
1340             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1341             nr_arena_item_unref (ai);
1342         }
1343         namedview->show(this);
1344         /* Ugly hack */
1345         activate_guides (true);
1346         /* Ugly hack */
1347         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1348     }
1350     _document_replaced_signal.emit (this, doc);
1352     View::setDocument (doc);
1355 void
1356 SPDesktop::onStatusMessage
1357 (Inkscape::MessageType type, gchar const *message)
1359     if (_widget) {
1360         _widget->setMessage(type, message);
1361     }
1364 void
1365 SPDesktop::onDocumentURISet (gchar const* uri)
1367     _widget->setTitle(uri);
1370 /**
1371  * Resized callback.
1372  */
1373 void
1374 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1376     _doc2dt[5] = height;
1377     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1378     NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1379     SP_CTRLRECT(page)->setRectangle(a);
1380     SP_CTRLRECT(page_border)->setRectangle(a);
1384 void
1385 SPDesktop::_onActivate (SPDesktop* dt)
1387     if (!dt->_widget) return;
1388     dt->_widget->activateDesktop();
1391 void
1392 SPDesktop::_onDeactivate (SPDesktop* dt)
1394     if (!dt->_widget) return;
1395     dt->_widget->deactivateDesktop();
1398 void
1399 SPDesktop::_onSelectionModified
1400 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1402     if (!dt->_widget) return;
1403     dt->_widget->updateScrollbars (expansion(dt->_d2w));
1406 static void
1407 _onSelectionChanged
1408 (Inkscape::Selection *selection, SPDesktop *desktop)
1410     /** \todo
1411      * only change the layer for single selections, or what?
1412      * This seems reasonable -- for multiple selections there can be many
1413      * different layers involved.
1414      */
1415     SPItem *item=selection->singleItem();
1416     if (item) {
1417         SPObject *layer=desktop->layerForObject(item);
1418         if ( layer && layer != desktop->currentLayer() ) {
1419             desktop->setCurrentLayer(layer);
1420         }
1421     }
1424 /**
1425  * Calls event handler of current event context.
1426  * \param arena Unused
1427  * \todo fixme
1428  */
1429 static gint
1430 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1432     if (ai) {
1433         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1434         return sp_event_context_item_handler (desktop->event_context, spi, event);
1435     } else {
1436         return sp_event_context_root_handler (desktop->event_context, event);
1437     }
1440 static void
1441 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1442     g_return_if_fail(SP_IS_GROUP(layer));
1443     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1446 /// Callback
1447 static void
1448 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1449     g_return_if_fail(SP_IS_GROUP(layer));
1450     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1453 /// Callback
1454 static void
1455 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1456                                          SPDesktop *desktop)
1458     desktop->_layer_changed_signal.emit (bottom);
1461 /// Called when document is starting to be rebuilt.
1462 static void
1463 _reconstruction_start (SPDesktop * desktop)
1465     // printf("Desktop, starting reconstruction\n");
1466     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1467     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1469     /*
1470     GSList const * selection_objs = desktop->selection->list();
1471     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1473     }
1474     */
1475     desktop->selection->clear();
1477     // printf("Desktop, starting reconstruction end\n");
1480 /// Called when document rebuild is finished.
1481 static void
1482 _reconstruction_finish (SPDesktop * desktop)
1484     // printf("Desktop, finishing reconstruction\n");
1485     if (desktop->_reconstruction_old_layer_id == NULL)
1486         return;
1488     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1489     if (newLayer != NULL)
1490         desktop->setCurrentLayer(newLayer);
1492     g_free(desktop->_reconstruction_old_layer_id);
1493     desktop->_reconstruction_old_layer_id = NULL;
1494     // printf("Desktop, finishing reconstruction end\n");
1495     return;
1498 /**
1499  * Namedview_modified callback.
1500  */
1501 static void
1502 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1504     SPNamedView *nv=SP_NAMEDVIEW(obj);
1506     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1508         /* Recalculate snap distances */
1509         /* FIXME: why is the desktop getting involved in setting up something
1510         ** that is entirely to do with the namedview?
1511         */
1512         _update_snap_distances (desktop);
1514         /* Show/hide page background */
1515         if (nv->pagecolor & 0xff) {
1516             sp_canvas_item_show (desktop->table);
1517             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1518             sp_canvas_item_move_to_z (desktop->table, 0);
1519         } else {
1520             sp_canvas_item_hide (desktop->table);
1521         }
1523         /* Show/hide page border */
1524         if (nv->showborder) {
1525             // show
1526             sp_canvas_item_show (desktop->page_border);
1527             // set color and shadow
1528             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1529             if (nv->pageshadow) {
1530                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1531             }
1532             // place in the z-order stack
1533             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1534                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1535             } else {
1536                 int order = sp_canvas_item_order (desktop->page_border);
1537                 int morder = sp_canvas_item_order (desktop->drawing);
1538                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1539                                     morder - order);
1540             }
1541         } else {
1542                 sp_canvas_item_hide (desktop->page_border);
1543                 if (nv->pageshadow) {
1544                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1545                 }
1546         }
1548         /* Show/hide page shadow */
1549         if (nv->showpageshadow && nv->pageshadow) {
1550             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1551         } else {
1552             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1553         }
1555         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1556             (SP_RGBA32_R_U(nv->pagecolor) +
1557              SP_RGBA32_G_U(nv->pagecolor) +
1558              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1559             // the background color is light or transparent, use black outline
1560             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1561         } else { // use white outline
1562             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1563         }
1564     }
1567 /**
1568  * Callback to reset snapper's distances.
1569  */
1570 static void
1571 _update_snap_distances (SPDesktop *desktop)
1573     SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1575     SPNamedView &nv = *desktop->namedview;
1577     //tell all grid snappers
1578     for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1579         Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1580         grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
1581                                                                       *nv.gridtoleranceunit,
1582                                                                       px));
1583     }
1585     nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
1586                                                                        *nv.guidetoleranceunit,
1587                                                                        px));
1588     nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
1589                                                                         *nv.objecttoleranceunit,
1590                                                                         px));
1594 NR::Matrix SPDesktop::w2d() const
1596     return _w2d;
1599 NR::Point SPDesktop::w2d(NR::Point const &p) const
1601     return p * _w2d;
1604 NR::Point SPDesktop::d2w(NR::Point const &p) const
1606     return p * _d2w;
1609 NR::Matrix SPDesktop::doc2dt() const
1611     return _doc2dt;
1614 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1616     return p * _doc2dt;
1619 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1621     return p / _doc2dt;
1625 /**
1626  * Pop event context from desktop's context stack. Never used.
1627  */
1628 // void
1629 // SPDesktop::pop_event_context (unsigned int key)
1630 // {
1631 //    SPEventContext *ec = NULL;
1632 //
1633 //    if (event_context && event_context->key == key) {
1634 //        g_return_if_fail (event_context);
1635 //        g_return_if_fail (event_context->next);
1636 //        ec = event_context;
1637 //        sp_event_context_deactivate (ec);
1638 //        event_context = ec->next;
1639 //        sp_event_context_activate (event_context);
1640 //        _event_context_changed_signal.emit (this, ec);
1641 //    }
1642 //
1643 //    SPEventContext *ref = event_context;
1644 //    while (ref && ref->next && ref->next->key != key)
1645 //        ref = ref->next;
1646 //
1647 //    if (ref && ref->next) {
1648 //        ec = ref->next;
1649 //        ref->next = ec->next;
1650 //    }
1651 //
1652 //    if (ec) {
1653 //        sp_event_context_finish (ec);
1654 //        g_object_unref (G_OBJECT (ec));
1655 //    }
1656 // }
1658 /*
1659   Local Variables:
1660   mode:c++
1661   c-file-style:"stroustrup"
1662   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1663   indent-tabs-mode:nil
1664   fill-column:99
1665   End:
1666 */
1667 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :