Code

Avoid crash by uninitialized perspectives.
[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 <2geom/rect.h>
61 #include "macros.h"
62 #include "inkscape-private.h"
63 #include "desktop.h"
64 #include "desktop-events.h"
65 #include "desktop-handles.h"
66 #include "document.h"
67 #include "message-stack.h"
68 #include "selection.h"
69 #include "select-context.h"
70 #include "sp-namedview.h"
71 #include "color.h"
72 #include "sp-item-group.h"
73 #include "preferences.h"
74 #include "object-hierarchy.h"
75 #include "helper/units.h"
76 #include "display/canvas-arena.h"
77 #include "display/nr-arena.h"
78 #include "display/gnome-canvas-acetate.h"
79 #include "display/sodipodi-ctrlrect.h"
80 #include "display/sp-canvas-util.h"
81 #include "display/canvas-temporary-item-list.h"
82 #include "display/snap-indicator.h"
83 #include "ui/dialog/dialog-manager.h"
84 #include "xml/repr.h"
85 #include "message-context.h"
86 #include "device-manager.h"
87 #include "layer-fns.h"
88 #include "layer-manager.h"
89 #include "event-log.h"
90 #include "display/canvas-grid.h"
91 #include "widgets/desktop-widget.h"
92 #include "box3d-context.h"
94 #include "display/sp-canvas.h"
96 namespace Inkscape { namespace XML { class Node; }}
98 // Callback declarations
99 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
100 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
101 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
102 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
103 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
104 static void _reconstruction_start(SPDesktop * desktop);
105 static void _reconstruction_finish(SPDesktop * desktop);
106 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
108 /**
109  * Return new desktop object.
110  * \pre namedview != NULL.
111  * \pre canvas != NULL.
112  */
113 SPDesktop::SPDesktop() :
114     _dlg_mgr( 0 ),
115     namedview( 0 ),
116     canvas( 0 ),
117     selection( 0 ),
118     event_context( 0 ),
119     layer_manager( 0 ),
120     event_log( 0 ),
121     temporary_item_list( 0 ),
122     snapindicator( 0 ),
123     acetate( 0 ),
124     main( 0 ),
125     gridgroup( 0 ),
126     guides( 0 ),
127     drawing( 0 ),
128     sketch( 0 ),
129     controls( 0 ),
130     tempgroup ( 0 ),
131     table( 0 ),
132     page( 0 ),
133     page_border( 0 ),
134     current( 0 ),
135     _focusMode(false),
136     zooms_past( 0 ),
137     zooms_future( 0 ),
138     dkey( 0 ),
139     number( 0 ),
140     window_state(0),
141     interaction_disabled_counter( 0 ),
142     waiting_cursor( false ),
143     guides_active( false ),
144     gr_item( 0 ),
145     gr_point_type( 0 ),
146     gr_point_i( 0 ),
147     gr_fill_or_stroke( true ),
148     _layer_hierarchy( 0 ),
149     _reconstruction_old_layer_id( 0 ),
150     _display_mode(Inkscape::RENDERMODE_NORMAL),
151     _widget( 0 ),
152     _inkscape( 0 ),
153     _guides_message_context( 0 ),
154     _active( false ),
155     _w2d(),
156     _d2w(),
157     _doc2dt( Geom::Scale(1, -1) ),
158     grids_visible( false )
160     _d2w.setIdentity();
161     _w2d.setIdentity();
163     selection = Inkscape::GC::release( new Inkscape::Selection(this) );
166 void
167 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas, Inkscape::UI::View::EditWidgetInterface *widget)
169     _widget = widget;
171     // Temporary workaround for link order issues:
172     Inkscape::DeviceManager::getManager().getDevices();
173     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
175     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
177     current = prefs->getStyle("/desktop/style");
179     namedview = nv;
180     canvas = aCanvas;
182     SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
183     /* Kill flicker */
184     sp_document_ensure_up_to_date (document);
186     /* Setup Dialog Manager */
187     _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
189     dkey = sp_item_display_key_new (1);
191     /* Connect document */
192     setDocument (document);
194     number = namedview->getViewCount();
197     /* Setup Canvas */
198     g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
200     SPCanvasGroup *root = sp_canvas_root (canvas);
202     /* Setup adminstrative layers */
203     acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
204     g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
205     main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
206     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
208     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
209     SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
210     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
211     sp_canvas_item_move_to_z (table, 0);
213     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
214     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
215     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
217     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
218     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
220     SP_CANVAS_ARENA (drawing)->arena->delta = prefs->getDouble("/options/cursortolerance/value", 1.0); // default is 1 px
222     if (prefs->getBool("/options/startmode/outline")) {
223         // Start in outline mode
224         setDisplayModeOutline();
225     } else {
226         // Start in normal mode, default
227         setDisplayModeNormal();
228     }
230     // The order in which these canvas items are added determines the z-order. It's therefore
231     // important to add the tempgroup (which will contain the snapindicator) before adding the
232     // controls. Only this way one will be able to quickly (before the snap indicator has
233     // disappeared) reselect a node after snapping it. If the z-order is wrong however, this
234     // will not work (the snap indicator is on top of the node handler; is the snapindicator
235     // being selected? or does it intercept some of the events that should have gone to the
236     // node handler? see bug https://bugs.launchpad.net/inkscape/+bug/414142)
237     gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
238     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
239     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
240     tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
241     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
243     /* Push select tool to the bottom of stack */
244     /** \todo
245      * FIXME: this is the only call to this.  Everything else seems to just
246      * call "set" instead of "push".  Can we assume that there is only one
247      * context ever?
248      */
249     push_event_context (SP_TYPE_SELECT_CONTEXT, "/tools/select", SP_EVENT_CONTEXT_STATIC);
251     // display rect and zoom are now handled in sp_desktop_widget_realize()
253     Geom::Rect const d(Geom::Point(0.0, 0.0),
254                        Geom::Point(sp_document_width(document), sp_document_height(document)));
256     SP_CTRLRECT(page)->setRectangle(d);
257     SP_CTRLRECT(page_border)->setRectangle(d);
259     /* the following sets the page shadow on the canvas
260        It was originally set to 5, which is really cheesy!
261        It now is an attribute in the document's namedview. If a value of
262        0 is used, then the constructor for a shadow is not initialized.
263     */
265     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
266         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
267     }
270     /* Connect event for page resize */
271     _doc2dt[5] = sp_document_height (document);
272     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
274     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
276     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
277             SP_CANVAS_ARENA (drawing)->arena,
278             dkey,
279             SP_ITEM_SHOW_DISPLAY);
280     if (ai) {
281         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
282     }
284     namedview->show(this);
285     /* Ugly hack */
286     activate_guides (true);
287     /* Ugly hack */
288     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
290 /* Set up notification of rebuilding the document, this allows
291        for saving object related settings in the document. */
292     _reconstruction_start_connection =
293         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
294     _reconstruction_finish_connection =
295         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
296     _reconstruction_old_layer_id = NULL;
298     // ?
299     // sp_active_desktop_set (desktop);
300     _inkscape = INKSCAPE;
302     _activate_connection = _activate_signal.connect(
303         sigc::bind(
304             sigc::ptr_fun(_onActivate),
305             this
306         )
307     );
308      _deactivate_connection = _deactivate_signal.connect(
309         sigc::bind(
310             sigc::ptr_fun(_onDeactivate),
311             this
312         )
313     );
315     _sel_modified_connection = selection->connectModified(
316         sigc::bind(
317             sigc::ptr_fun(&_onSelectionModified),
318             this
319         )
320     );
321     _sel_changed_connection = selection->connectChanged(
322         sigc::bind(
323             sigc::ptr_fun(&_onSelectionChanged),
324             this
325         )
326     );
329     /* setup LayerManager */
330     //   (Setting up after the connections are all in place, as it may use some of them)
331     layer_manager = new Inkscape::LayerManager( this );
333     showGrids(namedview->grids_visible, false);
335     temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
336     snapindicator = new Inkscape::Display::SnapIndicator ( this );
340 void SPDesktop::destroy()
342     if (snapindicator) {
343         delete snapindicator;
344         snapindicator = NULL;
345     }
346     if (temporary_item_list) {
347         delete temporary_item_list;
348         temporary_item_list = NULL;
349     }
351     if (selection) {
352         delete selection;
353         selection = NULL;
354     }
356     namedview->hide(this);
358     _activate_connection.disconnect();
359     _deactivate_connection.disconnect();
360     _sel_modified_connection.disconnect();
361     _sel_changed_connection.disconnect();
362     _modified_connection.disconnect();
363     _commit_connection.disconnect();
364     _reconstruction_start_connection.disconnect();
365     _reconstruction_finish_connection.disconnect();
367     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
368     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
369     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
371     while (event_context) {
372         SPEventContext *ec = event_context;
373         event_context = ec->next;
374         sp_event_context_finish (ec);
375         g_object_unref (G_OBJECT (ec));
376     }
378     if (_layer_hierarchy) {
379         delete _layer_hierarchy;
380 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
381     }
383     if (layer_manager) {
384         delete layer_manager;
385         layer_manager = NULL;
386     }
388     if (_inkscape) {
389         _inkscape = NULL;
390     }
392     if (drawing) {
393         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
394         drawing = NULL;
395     }
397     delete _guides_message_context;
398     _guides_message_context = NULL;
400     g_list_free (zooms_past);
401     g_list_free (zooms_future);
404 SPDesktop::~SPDesktop() {}
406 //--------------------------------------------------------------------
407 /* Public methods */
410 /* These methods help for temporarily showing things on-canvas.
411  * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
412  * is when you want to prematurely remove the item from the canvas, by calling
413  * desktop->remove_temporary_canvasitem(tempitem).
414  */
415 /** Note that lifetime is measured in milliseconds
416  * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
417  * delete the object for you and the reference will become invalid without you knowing it.
418  * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
419  * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
420  * because the object might be deleted already without you knowing it.
421  * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
422  */
423 Inkscape::Display::TemporaryItem *
424 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
426     if (move_to_bottom) {
427         sp_canvas_item_move_to_z(item, 0);
428     }
430     return temporary_item_list->add_item(item, lifetime);
433 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
434 */
435 void
436 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
438     // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
439     if (tempitem && temporary_item_list) {
440         temporary_item_list->delete_item(tempitem);
441     }
444 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
445     SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
446     canvas->rendermode = mode;
447     _display_mode = mode;
448     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
449     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
452 void SPDesktop::displayModeToggle() {
453     switch (_display_mode) {
454     case Inkscape::RENDERMODE_NORMAL:
455         _setDisplayMode(Inkscape::RENDERMODE_NO_FILTERS);
456         break;
457     case Inkscape::RENDERMODE_NO_FILTERS:
458         _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
459         break;
460     case Inkscape::RENDERMODE_OUTLINE:
461         _setDisplayMode(Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW);
462         break;
463     case Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW:
464     default:
465         _setDisplayMode(Inkscape::RENDERMODE_NORMAL);
466     }
469 /**
470  * Returns current root (=bottom) layer.
471  */
472 SPObject *SPDesktop::currentRoot() const
474     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
477 /**
478  * Returns current top layer.
479  */
480 SPObject *SPDesktop::currentLayer() const
482     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
485 /**
486  * Sets the current layer of the desktop.
487  *
488  * Make \a object the top layer.
489  */
490 void SPDesktop::setCurrentLayer(SPObject *object) {
491     g_return_if_fail(SP_IS_GROUP(object));
492     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
493     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
494     _layer_hierarchy->setBottom(object);
497 void SPDesktop::toggleLayerSolo(SPObject *object) {
498     g_return_if_fail(SP_IS_GROUP(object));
499     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
501     bool othersShowing = false;
502     std::vector<SPObject*> layers;
503     for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
504         layers.push_back(obj);
505         othersShowing |= !SP_ITEM(obj)->isHidden();
506     }
507     for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
508         layers.push_back(obj);
509         othersShowing |= !SP_ITEM(obj)->isHidden();
510     }
513     if ( SP_ITEM(object)->isHidden() ) {
514         SP_ITEM(object)->setHidden(false);
515     }
517     for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
518         SP_ITEM(*it)->setHidden(othersShowing);
519     }
522 /**
523  * Return layer that contains \a object.
524  */
525 SPObject *SPDesktop::layerForObject(SPObject *object) {
526     g_return_val_if_fail(object != NULL, NULL);
528     SPObject *root=currentRoot();
529     object = SP_OBJECT_PARENT(object);
530     while ( object && object != root && !isLayer(object) ) {
531         object = SP_OBJECT_PARENT(object);
532     }
533     return object;
536 /**
537  * True if object is a layer.
538  */
539 bool SPDesktop::isLayer(SPObject *object) const {
540     return ( SP_IS_GROUP(object)
541              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
542                   == SPGroup::LAYER ) );
545 /**
546  * True if desktop viewport fully contains \a item's bbox.
547  */
548 bool SPDesktop::isWithinViewport (SPItem *item) const
550     Geom::Rect const viewport = get_display_area();
551     Geom::OptRect const bbox = sp_item_bbox_desktop(item);
552     if (bbox) {
553         return viewport.contains(*bbox);
554     } else {
555         return true;
556     }
559 ///
560 bool SPDesktop::itemIsHidden(SPItem const *item) const {
561     return item->isHidden(this->dkey);
564 /**
565  * Set activate property of desktop; emit signal if changed.
566  */
567 void
568 SPDesktop::set_active (bool new_active)
570     if (new_active != _active) {
571         _active = new_active;
572         if (new_active) {
573             _activate_signal.emit();
574         } else {
575             _deactivate_signal.emit();
576         }
577     }
580 /**
581  * Set activate status of current desktop's named view.
582  */
583 void
584 SPDesktop::activate_guides(bool activate)
586     guides_active = activate;
587     namedview->activateGuides(this, activate);
590 /**
591  * Make desktop switch documents.
592  */
593 void
594 SPDesktop::change_document (SPDocument *theDocument)
596     g_return_if_fail (theDocument != NULL);
598     /* unselect everything before switching documents */
599     selection->clear();
601     setDocument (theDocument);
603     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
604        (this can probably be done in a better way) */
605     Gtk::Window *parent = this->getToplevel();
606     g_assert(parent != NULL);
607     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
608     if (dtw) {
609         dtw->desktop = this;
610     }
611     dtw->updateNamedview();
613     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
614     _document_replaced_signal.emit (this, theDocument);
617 /**
618  * Make desktop switch event contexts.
619  */
620 void
621 SPDesktop::set_event_context (GtkType type, const gchar *config)
623     SPEventContext *ec;
624     while (event_context) {
625         ec = event_context;
626         sp_event_context_deactivate (ec);
627         event_context = ec->next;
628         sp_event_context_finish (ec);
629         g_object_unref (G_OBJECT (ec));
630     }
632     ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
633     ec->next = event_context;
634     event_context = ec;
635     sp_event_context_activate (ec);
636     _event_context_changed_signal.emit (this, ec);
639 /**
640  * Push event context onto desktop's context stack.
641  */
642 void
643 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
645     SPEventContext *ref, *ec;
647     if (event_context && event_context->key == key) return;
648     ref = event_context;
649     while (ref && ref->next && ref->next->key != key) ref = ref->next;
650     if (ref && ref->next) {
651         ec = ref->next;
652         ref->next = ec->next;
653         sp_event_context_finish (ec);
654         g_object_unref (G_OBJECT (ec));
655     }
657     if (event_context) sp_event_context_deactivate (event_context);
658     ec = sp_event_context_new (type, this, config, key);
659     ec->next = event_context;
660     event_context = ec;
661     sp_event_context_activate (ec);
662     _event_context_changed_signal.emit (this, ec);
665 /**
666  * Sets the coordinate status to a given point
667  */
668 void
669 SPDesktop::set_coordinate_status (Geom::Point p) {
670     _widget->setCoordinateStatus(p);
673 /**
674  * \see sp_document_item_from_list_at_point_bottom()
675  */
676 SPItem *
677 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
679     g_return_val_if_fail (doc() != NULL, NULL);
680     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
683 /**
684  * \see sp_document_item_at_point()
685  */
686 SPItem *
687 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
689     g_return_val_if_fail (doc() != NULL, NULL);
690     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
693 /**
694  * \see sp_document_group_at_point()
695  */
696 SPItem *
697 SPDesktop::group_at_point (Geom::Point const p) const
699     g_return_val_if_fail (doc() != NULL, NULL);
700     return sp_document_group_at_point (doc(), dkey, p);
703 /**
704  * \brief  Returns the mouse point in document coordinates; if mouse is
705  * outside the canvas, returns the center of canvas viewpoint
706  */
707 Geom::Point
708 SPDesktop::point() const
710     Geom::Point p = _widget->getPointer();
711     Geom::Point pw = sp_canvas_window_to_world (canvas, p);
712     p = w2d(pw);
714     Geom::Rect const r = canvas->getViewbox();
716     Geom::Point r0 = w2d(r.min());
717     Geom::Point r1 = w2d(r.max());
719     if (p[Geom::X] >= r0[Geom::X] &&
720         p[Geom::X] <= r1[Geom::X] &&
721         p[Geom::Y] >= r1[Geom::Y] &&
722         p[Geom::Y] <= r0[Geom::Y])
723     {
724         return p;
725     } else {
726         return (r0 + r1) / 2;
727     }
730 /**
731  * Put current zoom data in history list.
732  */
733 void
734 SPDesktop::push_current_zoom (GList **history)
736     Geom::Rect const area = get_display_area();
738     NRRect *old_zoom = g_new(NRRect, 1);
739     old_zoom->x0 = area.min()[Geom::X];
740     old_zoom->x1 = area.max()[Geom::X];
741     old_zoom->y0 = area.min()[Geom::Y];
742     old_zoom->y1 = area.max()[Geom::Y];
743     if ( *history == NULL
744          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
745                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
746                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
747                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
748     {
749         *history = g_list_prepend (*history, old_zoom);
750     }
753 /**
754  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
755  */
756 void
757 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
759     g_assert(_widget);
761     // save the zoom
762     if (log) {
763         push_current_zoom(&zooms_past);
764         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
765         g_list_free (zooms_future);
766         zooms_future = NULL;
767     }
769     double const cx = 0.5 * (x0 + x1);
770     double const cy = 0.5 * (y0 + y1);
772     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
773     Geom::Rect viewbox = canvas->getViewbox();
774     viewbox.expandBy(-border);
776     double scale = _d2w.descrim();
777     double newscale;
778     if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
779         newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
780     } else {
781         newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
782     }
784     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
786     int clear = FALSE;
787     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
788         /* Set zoom factors */
789         _d2w = Geom::Scale(newscale, -newscale);
790         _w2d = Geom::Scale(1/newscale, 1/-newscale);
791         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
792         clear = TRUE;
793     }
795     /* Calculate top left corner (in document pixels) */
796     x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
797     y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
799     /* Scroll */
800     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
802     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
803     sp_box3d_context_update_lines(event_context);
805     _widget->updateRulers();
806     _widget->updateScrollbars(_d2w.descrim());
807     _widget->updateZoom();
810 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
812     set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
815 /**
816  * Return viewbox dimensions.
817  */
818 Geom::Rect SPDesktop::get_display_area() const
820     Geom::Rect const viewbox = canvas->getViewbox();
822     double const scale = _d2w[0];
824     return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
825                       Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
828 /**
829  * Revert back to previous zoom if possible.
830  */
831 void
832 SPDesktop::prev_zoom()
834     if (zooms_past == NULL) {
835         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
836         return;
837     }
839     // push current zoom into forward zooms list
840     push_current_zoom (&zooms_future);
842     // restore previous zoom
843     set_display_area (((NRRect *) zooms_past->data)->x0,
844             ((NRRect *) zooms_past->data)->y0,
845             ((NRRect *) zooms_past->data)->x1,
846             ((NRRect *) zooms_past->data)->y1,
847             0, false);
849     // remove the just-added zoom from the past zooms list
850     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
853 /**
854  * Set zoom to next in list.
855  */
856 void
857 SPDesktop::next_zoom()
859     if (zooms_future == NULL) {
860         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
861         return;
862     }
864     // push current zoom into past zooms list
865     push_current_zoom (&zooms_past);
867     // restore next zoom
868     set_display_area (((NRRect *) zooms_future->data)->x0,
869             ((NRRect *) zooms_future->data)->y0,
870             ((NRRect *) zooms_future->data)->x1,
871             ((NRRect *) zooms_future->data)->y1,
872             0, false);
874     // remove the just-used zoom from the zooms_future list
875     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
878 #include "tools-switch.h"
879 #include "node-context.h"
880 #include "shape-editor.h"
881 #include "nodepath.h"
883 /** \brief  Performs a quick zoom into what the user is working on
884     \param  enable  Whether we're going in or out of quick zoom
886 */
887 void
888 SPDesktop::zoom_quick (bool enable)
890     if (enable == _quick_zoom_enabled) {
891         return;
892     }
894     if (enable == true) {
895         _quick_zoom_stored_area = get_display_area();
896         bool zoomed = false;
898         if (!zoomed) {
899             SPItem * singleItem = selection->singleItem();
900             if (singleItem != NULL && tools_isactive(this, TOOLS_NODES)) {
902                 Inkscape::NodePath::Path * nodepath = event_context->shape_editor->get_nodepath();
903                 // printf("I've got a nodepath, crazy\n");
905                                 if (nodepath) {
906                                         Geom::Rect nodes;
907                                         bool firstnode = true;
909                                         if (nodepath->selected) {
910                                                 for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
911                                                    Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
912                                                         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
913                                                            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
914                                                                 if (node->selected) {
915                                                                         // printf("\tSelected node\n");
916                                                                         if (firstnode) {
917                                                                                 nodes = Geom::Rect(node->pos, node->pos);
918                                                                                 firstnode = false;
919                                                                         } else {
920                                                                                 nodes.expandTo(node->pos);
921                                                                         }
923                                                                         if (node->p.other != NULL) {
924                                                                                 /* Include previous node pos */
925                                                                                 nodes.expandTo(node->p.other->pos);
927                                                                                 /* Include previous handle */
928                                                                                 if (!sp_node_side_is_line(node, &node->p)) {
929                                                                                         nodes.expandTo(node->p.pos);
930                                                                                 }
931                                                                         }
933                                                                         if (node->n.other != NULL) {
934                                                                                 /* Include previous node pos */
935                                                                                 nodes.expandTo(node->n.other->pos);
937                                                                                 /* Include previous handle */
938                                                                                 if (!sp_node_side_is_line(node, &node->n)) {
939                                                                                         nodes.expandTo(node->n.pos);
940                                                                                 }
941                                                                         }
942                                                                 }
943                                                         }
944                                                 }
946                                                 if (!firstnode && nodes.area() * 2.0 < _quick_zoom_stored_area.area()) {
947                                                         set_display_area(nodes, 10);
948                                                         zoomed = true;
949                                                 }
950                                         }
951                                 }
952             }
953         }
955         if (!zoomed) {
956             Geom::OptRect const d = selection->bounds();
957             if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
958                 set_display_area(*d, 10);
959                 zoomed = true;
960             }
961         }
963         if (!zoomed) {
964             zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
965             zoomed = true;
966         }
967     } else {
968         set_display_area(_quick_zoom_stored_area, 0);
969     }
971     _quick_zoom_enabled = enable;
972     return;
975 /**
976  * Zoom to point with absolute zoom factor.
977  */
978 void
979 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
981     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
983     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
984     // this check prevents "sliding" when trying to zoom in at maximum zoom;
985     /// \todo someone please fix calculations properly and remove this hack
986     if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
987         return;
989     Geom::Rect const viewbox = canvas->getViewbox();
991     double const width2 = viewbox.dimensions()[Geom::X] / zoom;
992     double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
994     set_display_area(cx - px * width2,
995                      cy - py * height2,
996                      cx + (1 - px) * width2,
997                      cy + (1 - py) * height2,
998                      0.0);
1001 /**
1002  * Zoom to center with absolute zoom factor.
1003  */
1004 void
1005 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
1007     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
1010 /**
1011  * Zoom to point with relative zoom factor.
1012  */
1013 void
1014 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
1016     Geom::Rect const area = get_display_area();
1018     if (cx < area.min()[Geom::X]) {
1019         cx = area.min()[Geom::X];
1020     }
1021     if (cx > area.max()[Geom::X]) {
1022         cx = area.max()[Geom::X];
1023     }
1024     if (cy < area.min()[Geom::Y]) {
1025         cy = area.min()[Geom::Y];
1026     }
1027     if (cy > area.max()[Geom::Y]) {
1028         cy = area.max()[Geom::Y];
1029     }
1031     gdouble const scale = _d2w.descrim() * zoom;
1032     double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
1033     double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
1035     zoom_absolute_keep_point(cx, cy, px, py, scale);
1038 /**
1039  * Zoom to center with relative zoom factor.
1040  */
1041 void
1042 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1044     gdouble scale = _d2w.descrim() * zoom;
1045     zoom_absolute (cx, cy, scale);
1048 /**
1049  * Set display area to origin and current document dimensions.
1050  */
1051 void
1052 SPDesktop::zoom_page()
1054     Geom::Rect d(Geom::Point(0, 0),
1055                  Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1057     if (d.minExtent() < 1.0) {
1058         return;
1059     }
1061     set_display_area(d, 10);
1064 /**
1065  * Set display area to current document width.
1066  */
1067 void
1068 SPDesktop::zoom_page_width()
1070     Geom::Rect const a = get_display_area();
1072     if (sp_document_width(doc()) < 1.0) {
1073         return;
1074     }
1076     Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1077                  Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1079     set_display_area(d, 10);
1082 /**
1083  * Zoom to selection.
1084  */
1085 void
1086 SPDesktop::zoom_selection()
1088     Geom::OptRect const d = selection->bounds();
1090     if ( !d || d->minExtent() < 0.1 ) {
1091         return;
1092     }
1094     set_display_area(*d, 10);
1097 /**
1098  * Tell widget to let zoom widget grab keyboard focus.
1099  */
1100 void
1101 SPDesktop::zoom_grab_focus()
1103     _widget->letZoomGrabFocus();
1106 /**
1107  * Zoom to whole drawing.
1108  */
1109 void
1110 SPDesktop::zoom_drawing()
1112     g_return_if_fail (doc() != NULL);
1113     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1114     g_return_if_fail (docitem != NULL);
1116     Geom::OptRect d = sp_item_bbox_desktop(docitem);
1118     /* Note that the second condition here indicates that
1119     ** there are no items in the drawing.
1120     */
1121     if ( !d || d->minExtent() < 0.1 ) {
1122         return;
1123     }
1125     set_display_area(*d, 10);
1128 /**
1129  * Scroll canvas by specific coordinate amount in svg coordinates.
1130  */
1131 void
1132 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1134     double scale = _d2w.descrim();
1135     scroll_world(dx*scale, dy*scale, is_scrolling);
1138 /**
1139  * Scroll canvas by specific coordinate amount.
1140  */
1141 void
1142 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1144     g_assert(_widget);
1146     Geom::Rect const viewbox = canvas->getViewbox();
1148     sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1150     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1151     sp_box3d_context_update_lines(event_context);
1153     _widget->updateRulers();
1154     _widget->updateScrollbars(_d2w.descrim());
1157 bool
1158 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1160     using Geom::X;
1161     using Geom::Y;
1163     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1164     gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1166     // autoscrolldistance is in screen pixels, but the display area is in document units
1167     autoscrolldistance /= _d2w.descrim();
1168     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1169     Geom::Rect dbox = get_display_area();
1170     dbox.expandBy(-autoscrolldistance);
1172     if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1173         !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y])   ) {
1175         Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1177         gdouble x_to;
1178         if (p[X] < dbox.min()[X])
1179             x_to = dbox.min()[X];
1180         else if (p[X] > dbox.max()[X])
1181             x_to = dbox.max()[X];
1182         else
1183             x_to = p[X];
1185         gdouble y_to;
1186         if (p[Y] < dbox.min()[Y])
1187             y_to = dbox.min()[Y];
1188         else if (p[Y] > dbox.max()[Y])
1189             y_to = dbox.max()[Y];
1190         else
1191             y_to = p[Y];
1193         Geom::Point const d_dt(x_to, y_to);
1194         Geom::Point const d_w( d_dt * _d2w );
1195         Geom::Point const moved_w( d_w - s_w );
1197         if (autoscrollspeed == 0)
1198             autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1200         if (autoscrollspeed != 0)
1201             scroll_world (autoscrollspeed * moved_w);
1203         return true;
1204     }
1205     return false;
1208 bool
1209 SPDesktop::is_iconified()
1211     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1214 void
1215 SPDesktop::iconify()
1217     _widget->setIconified();
1220 bool
1221 SPDesktop::is_maximized()
1223     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1226 void
1227 SPDesktop::maximize()
1229     _widget->setMaximized();
1232 bool
1233 SPDesktop::is_fullscreen()
1235     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1238 void
1239 SPDesktop::fullscreen()
1241     _widget->setFullscreen();
1244 /** \brief  Checks to see if the user is working in focused mode
1246     Returns the value of \c _focusMode
1247 */
1248 bool
1249 SPDesktop::is_focusMode()
1251     return _focusMode;
1254 /** \brief  Changes whether the user is in focus mode or not
1255     \param  mode  Which mode the view should be in
1257 */
1258 void
1259 SPDesktop::focusMode (bool mode)
1261     if (mode == _focusMode) { return; }
1263     _focusMode = mode;
1265     layoutWidget();
1266     //sp_desktop_widget_layout(SPDesktopWidget);
1268     return;
1271 void
1272 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1274     _widget->getGeometry (x, y, w, h);
1277 void
1278 SPDesktop::setWindowPosition (Geom::Point p)
1280     _widget->setPosition (p);
1283 void
1284 SPDesktop::setWindowSize (gint w, gint h)
1286     _widget->setSize (w, h);
1289 void
1290 SPDesktop::setWindowTransient (void *p, int transient_policy)
1292     _widget->setTransient (p, transient_policy);
1295 Gtk::Window*
1296 SPDesktop::getToplevel( )
1298     return _widget->getWindow();
1301 void
1302 SPDesktop::presentWindow()
1304     _widget->present();
1307 bool
1308 SPDesktop::warnDialog (gchar *text)
1310     return _widget->warnDialog (text);
1313 void
1314 SPDesktop::toggleRulers()
1316     _widget->toggleRulers();
1319 void
1320 SPDesktop::toggleScrollbars()
1322     _widget->toggleScrollbars();
1325 void
1326 SPDesktop::layoutWidget()
1328     _widget->layout();
1331 void
1332 SPDesktop::destroyWidget()
1334     _widget->destroy();
1337 bool
1338 SPDesktop::shutdown()
1340     return _widget->shutdown();
1343 bool SPDesktop::onDeleteUI (GdkEventAny*)
1345     if(shutdown())
1346         return true;
1348     destroyWidget();
1349     return false;
1352 /**
1353  *  onWindowStateEvent
1354  *
1355  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1356  *  Since GTK doesn't have a way to query this state information directly, we
1357  *  record it for the desktop here, and also possibly trigger a layout.
1358  */
1359 bool
1360 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1362     // Record the desktop window's state
1363     window_state = event->new_window_state;
1365     // Layout may differ depending on full-screen mode or not
1366     GdkWindowState changed = event->changed_mask;
1367     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1368         layoutWidget();
1369     }
1371     return false;
1374 void
1375 SPDesktop::setToolboxFocusTo (gchar const *label)
1377     _widget->setToolboxFocusTo (label);
1380 void
1381 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1383     _widget->setToolboxAdjustmentValue (id, val);
1386 void
1387 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1389     _widget->setToolboxSelectOneValue (id, val);
1392 bool
1393 SPDesktop::isToolboxButtonActive (gchar const *id)
1395     return _widget->isToolboxButtonActive (id);
1398 void
1399 SPDesktop::emitToolSubselectionChanged(gpointer data)
1401     _tool_subselection_changed.emit(data);
1402     inkscape_subselection_changed (this);
1405 void
1406 SPDesktop::updateNow()
1408   sp_canvas_update_now(canvas);
1411 void
1412 SPDesktop::enableInteraction()
1414   _widget->enableInteraction();
1417 void SPDesktop::disableInteraction()
1419   _widget->disableInteraction();
1422 void SPDesktop::setWaitingCursor()
1424     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1425     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1426     gdk_cursor_unref(waiting);
1427     // GDK needs the flush for the cursor change to take effect
1428     gdk_flush();
1429     waiting_cursor = true;
1432 void SPDesktop::clearWaitingCursor()
1434   if (waiting_cursor)
1435       sp_event_context_update_cursor(sp_desktop_event_context(this));
1438 void SPDesktop::toggleColorProfAdjust()
1440     _widget->toggleColorProfAdjust();
1443 void SPDesktop::toggleGrids()
1445     if (namedview->grids) {
1446         if(gridgroup) {
1447             showGrids(!grids_visible);
1448         }
1449     } else {
1450         //there is no grid present at the moment. add a rectangular grid and make it visible
1451         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1452         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1453         showGrids(true);
1454     }
1457 void SPDesktop::showGrids(bool show, bool dirty_document)
1459     grids_visible = show;
1460     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1461     if (show) {
1462         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1463     } else {
1464         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1465     }
1468 void SPDesktop::toggleSnapGlobal()
1470     bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1471     Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1472     sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1475 //----------------------------------------------------------------------
1476 // Callback implementations. The virtual ones are connected by the view.
1478 void
1479 SPDesktop::onPositionSet (double x, double y)
1481     _widget->viewSetPosition (Geom::Point(x,y));
1484 void
1485 SPDesktop::onResized (double /*x*/, double /*y*/)
1487    // Nothing called here
1490 /**
1491  * Redraw callback; queues Gtk redraw; connected by View.
1492  */
1493 void
1494 SPDesktop::onRedrawRequested ()
1496     if (main) {
1497         _widget->requestCanvasUpdate();
1498     }
1501 void
1502 SPDesktop::updateCanvasNow()
1504   _widget->requestCanvasUpdateAndWait();
1507 /**
1508  * Associate document with desktop.
1509  */
1510 void
1511 SPDesktop::setDocument (SPDocument *doc)
1513     if (this->doc() && doc) {
1514         namedview->hide(this);
1515         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1516     }
1518     if (_layer_hierarchy) {
1519         _layer_hierarchy->clear();
1520         delete _layer_hierarchy;
1521     }
1522     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1523     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1524     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1525     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1526     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1528     /* setup EventLog */
1529     event_log = new Inkscape::EventLog(doc);
1530     doc->addUndoObserver(*event_log);
1532     _commit_connection.disconnect();
1533     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1535     /// \todo fixme: This condition exists to make sure the code
1536     /// inside is NOT called on initialization, only on replacement. But there
1537     /// are surely more safe methods to accomplish this.
1538     // TODO since the comment had reversed logic, check the intent of this block of code:
1539     if (drawing) {
1540         NRArenaItem *ai = 0;
1542         namedview = sp_document_namedview (doc, NULL);
1543         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1544         number = namedview->getViewCount();
1546         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1547                 SP_CANVAS_ARENA (drawing)->arena,
1548                 dkey,
1549                 SP_ITEM_SHOW_DISPLAY);
1550         if (ai) {
1551             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1552         }
1553         namedview->show(this);
1554         /* Ugly hack */
1555         activate_guides (true);
1556         /* Ugly hack */
1557         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1558     }
1560     _document_replaced_signal.emit (this, doc);
1562     View::setDocument (doc);
1565 void
1566 SPDesktop::onStatusMessage
1567 (Inkscape::MessageType type, gchar const *message)
1569     if (_widget) {
1570         _widget->setMessage(type, message);
1571     }
1574 void
1575 SPDesktop::onDocumentURISet (gchar const* uri)
1577     _widget->setTitle(uri);
1580 /**
1581  * Resized callback.
1582  */
1583 void
1584 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1586     _doc2dt[5] = height;
1587     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1588     Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1589     SP_CTRLRECT(page)->setRectangle(a);
1590     SP_CTRLRECT(page_border)->setRectangle(a);
1594 void
1595 SPDesktop::_onActivate (SPDesktop* dt)
1597     if (!dt->_widget) return;
1598     dt->_widget->activateDesktop();
1601 void
1602 SPDesktop::_onDeactivate (SPDesktop* dt)
1604     if (!dt->_widget) return;
1605     dt->_widget->deactivateDesktop();
1608 void
1609 SPDesktop::_onSelectionModified
1610 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1612     if (!dt->_widget) return;
1613     dt->_widget->updateScrollbars (dt->_d2w.descrim());
1616 static void
1617 _onSelectionChanged
1618 (Inkscape::Selection *selection, SPDesktop *desktop)
1620     /** \todo
1621      * only change the layer for single selections, or what?
1622      * This seems reasonable -- for multiple selections there can be many
1623      * different layers involved.
1624      */
1625     SPItem *item=selection->singleItem();
1626     if (item) {
1627         SPObject *layer=desktop->layerForObject(item);
1628         if ( layer && layer != desktop->currentLayer() ) {
1629             desktop->setCurrentLayer(layer);
1630         }
1631     }
1634 /**
1635  * Calls event handler of current event context.
1636  * \param arena Unused
1637  * \todo fixme
1638  */
1639 static gint
1640 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1642     if (ai) {
1643         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1644         return sp_event_context_item_handler (desktop->event_context, spi, event);
1645     } else {
1646         return sp_event_context_root_handler (desktop->event_context, event);
1647     }
1650 static void
1651 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1652     g_return_if_fail(SP_IS_GROUP(layer));
1653     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1656 /// Callback
1657 static void
1658 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1659     g_return_if_fail(SP_IS_GROUP(layer));
1660     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1663 /// Callback
1664 static void
1665 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1666                                          SPDesktop *desktop)
1668     desktop->_layer_changed_signal.emit (bottom);
1671 /// Called when document is starting to be rebuilt.
1672 static void
1673 _reconstruction_start (SPDesktop * desktop)
1675     // printf("Desktop, starting reconstruction\n");
1676     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1677     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1679     /*
1680     GSList const * selection_objs = desktop->selection->list();
1681     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1683     }
1684     */
1685     desktop->selection->clear();
1687     // printf("Desktop, starting reconstruction end\n");
1690 /// Called when document rebuild is finished.
1691 static void
1692 _reconstruction_finish (SPDesktop * desktop)
1694     // printf("Desktop, finishing reconstruction\n");
1695     if (desktop->_reconstruction_old_layer_id == NULL)
1696         return;
1698     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1699     if (newLayer != NULL)
1700         desktop->setCurrentLayer(newLayer);
1702     g_free(desktop->_reconstruction_old_layer_id);
1703     desktop->_reconstruction_old_layer_id = NULL;
1704     // printf("Desktop, finishing reconstruction end\n");
1705     return;
1708 /**
1709  * Namedview_modified callback.
1710  */
1711 static void
1712 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1714     SPNamedView *nv=SP_NAMEDVIEW(obj);
1716     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1718         /* Show/hide page background */
1719         if (nv->pagecolor & 0xff) {
1720             sp_canvas_item_show (desktop->table);
1721             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1722             sp_canvas_item_move_to_z (desktop->table, 0);
1723         } else {
1724             sp_canvas_item_hide (desktop->table);
1725         }
1727         /* Show/hide page border */
1728         if (nv->showborder) {
1729             // show
1730             sp_canvas_item_show (desktop->page_border);
1731             // set color and shadow
1732             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1733             if (nv->pageshadow) {
1734                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1735             }
1736             // place in the z-order stack
1737             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1738                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1739             } else {
1740                 int order = sp_canvas_item_order (desktop->page_border);
1741                 int morder = sp_canvas_item_order (desktop->drawing);
1742                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1743                     morder - order);
1744             }
1745         } else {
1746                 sp_canvas_item_hide (desktop->page_border);
1747                 if (nv->pageshadow) {
1748                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1749                 }
1750         }
1752         /* Show/hide page shadow */
1753         if (nv->showpageshadow && nv->pageshadow) {
1754             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1755         } else {
1756             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1757         }
1759         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1760         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1761             (SP_RGBA32_R_U(nv->pagecolor) +
1762              SP_RGBA32_G_U(nv->pagecolor) +
1763              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1764             // the background color is light or transparent, use black outline
1765             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1766         } else { // use white outline
1767             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1768         }
1769     }
1772 Geom::Matrix SPDesktop::w2d() const
1774     return _w2d;
1777 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1779     return p * _w2d;
1782 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1784     return p * _d2w;
1787 Geom::Matrix SPDesktop::doc2dt() const
1789     return _doc2dt;
1792 Geom::Matrix SPDesktop::dt2doc() const
1794     // doc2dt is its own inverse
1795     return _doc2dt;
1798 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1800     return p * _doc2dt;
1803 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1805     return p * dt2doc();
1809 /**
1810  * Pop event context from desktop's context stack. Never used.
1811  */
1812 // void
1813 // SPDesktop::pop_event_context (unsigned int key)
1814 // {
1815 //    SPEventContext *ec = NULL;
1816 //
1817 //    if (event_context && event_context->key == key) {
1818 //        g_return_if_fail (event_context);
1819 //        g_return_if_fail (event_context->next);
1820 //        ec = event_context;
1821 //        sp_event_context_deactivate (ec);
1822 //        event_context = ec->next;
1823 //        sp_event_context_activate (event_context);
1824 //        _event_context_changed_signal.emit (this, ec);
1825 //    }
1826 //
1827 //    SPEventContext *ref = event_context;
1828 //    while (ref && ref->next && ref->next->key != key)
1829 //        ref = ref->next;
1830 //
1831 //    if (ref && ref->next) {
1832 //        ec = ref->next;
1833 //        ref->next = ec->next;
1834 //    }
1835 //
1836 //    if (ec) {
1837 //        sp_event_context_finish (ec);
1838 //        g_object_unref (G_OBJECT (ec));
1839 //    }
1840 // }
1842 /*
1843   Local Variables:
1844   mode:c++
1845   c-file-style:"stroustrup"
1846   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1847   indent-tabs-mode:nil
1848   fill-column:99
1849   End:
1850 */
1851 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :