Code

Split SPCanvasItem and SPCanvasGroup to individual .h files. Removed forward header.
[inkscape.git] / src / desktop.cpp
1 /** \file
2  * Editable view implementation
3  *
4  * Authors:
5  *   Lauris Kaplinski <lauris@kaplinski.com>
6  *   MenTaLguY <mental@rydia.net>
7  *   bulia byak <buliabyak@users.sf.net>
8  *   Ralf Stephan <ralf@ark.in-berlin.de>
9  *   John Bintz <jcoswell@coswellproductions.org>
10  *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
11  *   Jon A. Cruz <jon@joncruz.org>
12  *   Abhishek Sharma
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 "display/sp-canvas-group.h"
84 #include "ui/dialog/dialog-manager.h"
85 #include "xml/repr.h"
86 #include "message-context.h"
87 #include "device-manager.h"
88 #include "layer-fns.h"
89 #include "layer-manager.h"
90 #include "event-log.h"
91 #include "display/canvas-grid.h"
92 #include "widgets/desktop-widget.h"
93 #include "box3d-context.h"
94 #include "desktop-style.h"
96 // TODO those includes are only for node tool quick zoom. Remove them after fixing it.
97 #include "ui/tool/node-tool.h"
98 #include "ui/tool/control-point-selection.h"
100 #include "display/sp-canvas.h"
102 namespace Inkscape { namespace XML { class Node; }}
104 // Callback declarations
105 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
106 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
107 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
108 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
109 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
110 static void _reconstruction_start(SPDesktop * desktop);
111 static void _reconstruction_finish(SPDesktop * desktop);
112 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
114 /**
115  * Return new desktop object.
116  * \pre namedview != NULL.
117  * \pre canvas != NULL.
118  */
119 SPDesktop::SPDesktop() :
120     _dlg_mgr( 0 ),
121     namedview( 0 ),
122     canvas( 0 ),
123     selection( 0 ),
124     event_context( 0 ),
125     layer_manager( 0 ),
126     event_log( 0 ),
127     temporary_item_list( 0 ),
128     snapindicator( 0 ),
129     acetate( 0 ),
130     main( 0 ),
131     gridgroup( 0 ),
132     guides( 0 ),
133     drawing( 0 ),
134     sketch( 0 ),
135     controls( 0 ),
136     tempgroup ( 0 ),
137     table( 0 ),
138     page( 0 ),
139     page_border( 0 ),
140     current( 0 ),
141     _focusMode(false),
142     zooms_past( 0 ),
143     zooms_future( 0 ),
144     dkey( 0 ),
145     number( 0 ),
146     window_state(0),
147     interaction_disabled_counter( 0 ),
148     waiting_cursor( false ),
149     guides_active( false ),
150     gr_item( 0 ),
151     gr_point_type( 0 ),
152     gr_point_i( 0 ),
153     gr_fill_or_stroke( true ),
154     _layer_hierarchy( 0 ),
155     _reconstruction_old_layer_id( 0 ),
156     _display_mode(Inkscape::RENDERMODE_NORMAL),
157     _widget( 0 ),
158     _inkscape( 0 ),
159     _guides_message_context( 0 ),
160     _active( false ),
161     _w2d(),
162     _d2w(),
163     _doc2dt( Geom::Scale(1, -1) ),
164     grids_visible( false )
166     _d2w.setIdentity();
167     _w2d.setIdentity();
169     selection = Inkscape::GC::release( new Inkscape::Selection(this) );
172 void
173 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas, Inkscape::UI::View::EditWidgetInterface *widget)
175     _widget = widget;
177     // Temporary workaround for link order issues:
178     Inkscape::DeviceManager::getManager().getDevices();
179     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
181     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
183     current = prefs->getStyle("/desktop/style");
185     namedview = nv;
186     canvas = aCanvas;
188     SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
189     /* Kill flicker */
190     document->ensureUpToDate();
192     /* Setup Dialog Manager */
193     _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
195     dkey = SPItem::display_key_new(1);
197     /* Connect document */
198     setDocument (document);
200     number = namedview->getViewCount();
203     /* Setup Canvas */
204     g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
206     SPCanvasGroup *root = sp_canvas_root (canvas);
208     /* Setup adminstrative layers */
209     acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
210     g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
211     main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
212     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
214     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
215     SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
216     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
217     sp_canvas_item_move_to_z (table, 0);
219     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
220     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
221     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
223     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
224     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
226     SP_CANVAS_ARENA (drawing)->arena->delta = prefs->getDouble("/options/cursortolerance/value", 1.0); // default is 1 px
228     if (prefs->getBool("/options/startmode/outline")) {
229         // Start in outline mode
230         setDisplayModeOutline();
231     } else {
232         // Start in normal mode, default
233         setDisplayModeNormal();
234     }
236     // The order in which these canvas items are added determines the z-order. It's therefore
237     // important to add the tempgroup (which will contain the snapindicator) before adding the
238     // controls. Only this way one will be able to quickly (before the snap indicator has
239     // disappeared) reselect a node after snapping it. If the z-order is wrong however, this
240     // will not work (the snap indicator is on top of the node handler; is the snapindicator
241     // being selected? or does it intercept some of the events that should have gone to the
242     // node handler? see bug https://bugs.launchpad.net/inkscape/+bug/414142)
243     gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
244     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
245     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
246     tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
247     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
249     /* Push select tool to the bottom of stack */
250     /** \todo
251      * FIXME: this is the only call to this.  Everything else seems to just
252      * call "set" instead of "push".  Can we assume that there is only one
253      * context ever?
254      */
255     push_event_context (SP_TYPE_SELECT_CONTEXT, "/tools/select", SP_EVENT_CONTEXT_STATIC);
257     // display rect and zoom are now handled in sp_desktop_widget_realize()
259     Geom::Rect const d(Geom::Point(0.0, 0.0),
260                        Geom::Point(document->getWidth(), document->getHeight()));
262     SP_CTRLRECT(page)->setRectangle(d);
263     SP_CTRLRECT(page_border)->setRectangle(d);
265     /* the following sets the page shadow on the canvas
266        It was originally set to 5, which is really cheesy!
267        It now is an attribute in the document's namedview. If a value of
268        0 is used, then the constructor for a shadow is not initialized.
269     */
271     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
272         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
273     }
276     /* Connect event for page resize */
277     _doc2dt[5] = document->getHeight();
278     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
280     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
282     NRArenaItem *ai = SP_ITEM(document->getRoot())->invoke_show(
283             SP_CANVAS_ARENA (drawing)->arena,
284             dkey,
285             SP_ITEM_SHOW_DISPLAY);
286     if (ai) {
287         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
288     }
290     namedview->show(this);
291     /* Ugly hack */
292     activate_guides (true);
293     /* Ugly hack */
294     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
296 /* Set up notification of rebuilding the document, this allows
297        for saving object related settings in the document. */
298     _reconstruction_start_connection =
299         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
300     _reconstruction_finish_connection =
301         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
302     _reconstruction_old_layer_id = NULL;
304     // ?
305     // sp_active_desktop_set (desktop);
306     _inkscape = INKSCAPE;
308     _activate_connection = _activate_signal.connect(
309         sigc::bind(
310             sigc::ptr_fun(_onActivate),
311             this
312         )
313     );
314      _deactivate_connection = _deactivate_signal.connect(
315         sigc::bind(
316             sigc::ptr_fun(_onDeactivate),
317             this
318         )
319     );
321     _sel_modified_connection = selection->connectModified(
322         sigc::bind(
323             sigc::ptr_fun(&_onSelectionModified),
324             this
325         )
326     );
327     _sel_changed_connection = selection->connectChanged(
328         sigc::bind(
329             sigc::ptr_fun(&_onSelectionChanged),
330             this
331         )
332     );
335     /* setup LayerManager */
336     //   (Setting up after the connections are all in place, as it may use some of them)
337     layer_manager = new Inkscape::LayerManager( this );
339     showGrids(namedview->grids_visible, false);
341     temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
342     snapindicator = new Inkscape::Display::SnapIndicator ( this );
346 void SPDesktop::destroy()
348     if (snapindicator) {
349         delete snapindicator;
350         snapindicator = NULL;
351     }
352     if (temporary_item_list) {
353         delete temporary_item_list;
354         temporary_item_list = NULL;
355     }
357     if (selection) {
358         delete selection;
359         selection = NULL;
360     }
362     namedview->hide(this);
364     _activate_connection.disconnect();
365     _deactivate_connection.disconnect();
366     _sel_modified_connection.disconnect();
367     _sel_changed_connection.disconnect();
368     _modified_connection.disconnect();
369     _commit_connection.disconnect();
370     _reconstruction_start_connection.disconnect();
371     _reconstruction_finish_connection.disconnect();
373     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
374     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
375     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
377     while (event_context) {
378         SPEventContext *ec = event_context;
379         event_context = ec->next;
380         sp_event_context_finish (ec);
381         g_object_unref (G_OBJECT (ec));
382     }
384     if (_layer_hierarchy) {
385         delete _layer_hierarchy;
386 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
387     }
389     if (layer_manager) {
390         delete layer_manager;
391         layer_manager = NULL;
392     }
394     if (_inkscape) {
395         _inkscape = NULL;
396     }
398     if (drawing) {
399         SP_ITEM(doc()->getRoot())->invoke_hide(dkey);
400         drawing = NULL;
401     }
403     delete _guides_message_context;
404     _guides_message_context = NULL;
406     g_list_free (zooms_past);
407     g_list_free (zooms_future);
410 SPDesktop::~SPDesktop() {}
412 //--------------------------------------------------------------------
413 /* Public methods */
416 /* These methods help for temporarily showing things on-canvas.
417  * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
418  * is when you want to prematurely remove the item from the canvas, by calling
419  * desktop->remove_temporary_canvasitem(tempitem).
420  */
421 /** Note that lifetime is measured in milliseconds
422  * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
423  * delete the object for you and the reference will become invalid without you knowing it.
424  * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
425  * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
426  * because the object might be deleted already without you knowing it.
427  * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
428  */
429 Inkscape::Display::TemporaryItem *
430 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
432     if (move_to_bottom) {
433         sp_canvas_item_move_to_z(item, 0);
434     }
436     return temporary_item_list->add_item(item, lifetime);
439 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
440 */
441 void
442 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
444     // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
445     if (tempitem && temporary_item_list) {
446         temporary_item_list->delete_item(tempitem);
447     }
450 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
451     SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
452     canvas->rendermode = mode;
453     _display_mode = mode;
454     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
455     _widget->setTitle( sp_desktop_document(this)->getName() );
458 void SPDesktop::displayModeToggle() {
459     switch (_display_mode) {
460     case Inkscape::RENDERMODE_NORMAL:
461         _setDisplayMode(Inkscape::RENDERMODE_NO_FILTERS);
462         break;
463     case Inkscape::RENDERMODE_NO_FILTERS:
464         _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
465         break;
466     case Inkscape::RENDERMODE_OUTLINE:
467         _setDisplayMode(Inkscape::RENDERMODE_NORMAL);
468         break;
469 //    case Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW:
470     default:
471         _setDisplayMode(Inkscape::RENDERMODE_NORMAL);
472     }
475 /**
476  * Returns current root (=bottom) layer.
477  */
478 SPObject *SPDesktop::currentRoot() const
480     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
483 /**
484  * Returns current top layer.
485  */
486 SPObject *SPDesktop::currentLayer() const
488     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
491 /**
492  * Sets the current layer of the desktop.
493  *
494  * Make \a object the top layer.
495  */
496 void SPDesktop::setCurrentLayer(SPObject *object) {
497     g_return_if_fail(SP_IS_GROUP(object));
498     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
499     // printf("Set Layer to ID: %s\n", object->getId());
500     _layer_hierarchy->setBottom(object);
503 void SPDesktop::toggleLayerSolo(SPObject *object) {
504     g_return_if_fail(SP_IS_GROUP(object));
505     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
507     bool othersShowing = false;
508     std::vector<SPObject*> layers;
509     for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
510         layers.push_back(obj);
511         othersShowing |= !SP_ITEM(obj)->isHidden();
512     }
513     for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
514         layers.push_back(obj);
515         othersShowing |= !SP_ITEM(obj)->isHidden();
516     }
519     if ( SP_ITEM(object)->isHidden() ) {
520         SP_ITEM(object)->setHidden(false);
521     }
523     for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
524         SP_ITEM(*it)->setHidden(othersShowing);
525     }
528 /**
529  * Return layer that contains \a object.
530  */
531 SPObject *SPDesktop::layerForObject(SPObject *object) {
532     g_return_val_if_fail(object != NULL, NULL);
534     SPObject *root=currentRoot();
535     object = SP_OBJECT_PARENT(object);
536     while ( object && object != root && !isLayer(object) ) {
537         object = SP_OBJECT_PARENT(object);
538     }
539     return object;
542 /**
543  * True if object is a layer.
544  */
545 bool SPDesktop::isLayer(SPObject *object) const {
546     return ( SP_IS_GROUP(object)
547              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
548                   == SPGroup::LAYER ) );
551 /**
552  * True if desktop viewport fully contains \a item's bbox.
553  */
554 bool SPDesktop::isWithinViewport (SPItem *item) const
556     Geom::Rect const viewport = get_display_area();
557     Geom::OptRect const bbox = item->getBboxDesktop();
558     if (bbox) {
559         return viewport.contains(*bbox);
560     } else {
561         return true;
562     }
565 ///
566 bool SPDesktop::itemIsHidden(SPItem const *item) const {
567     return item->isHidden(this->dkey);
570 /**
571  * Set activate property of desktop; emit signal if changed.
572  */
573 void
574 SPDesktop::set_active (bool new_active)
576     if (new_active != _active) {
577         _active = new_active;
578         if (new_active) {
579             _activate_signal.emit();
580         } else {
581             _deactivate_signal.emit();
582         }
583     }
586 /**
587  * Set activate status of current desktop's named view.
588  */
589 void
590 SPDesktop::activate_guides(bool activate)
592     guides_active = activate;
593     namedview->activateGuides(this, activate);
596 /**
597  * Make desktop switch documents.
598  */
599 void
600 SPDesktop::change_document (SPDocument *theDocument)
602     g_return_if_fail (theDocument != NULL);
604     /* unselect everything before switching documents */
605     selection->clear();
607     setDocument (theDocument);
609     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
610        (this can probably be done in a better way) */
611     Gtk::Window *parent = this->getToplevel();
612     g_assert(parent != NULL);
613     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
614     if (dtw) {
615         dtw->desktop = this;
616     }
617     dtw->updateNamedview();
619     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
620     _document_replaced_signal.emit (this, theDocument);
623 /**
624  * Make desktop switch event contexts.
625  */
626 void
627 SPDesktop::set_event_context (GtkType type, const gchar *config)
629     SPEventContext *ec;
630     while (event_context) {
631         ec = event_context;
632         sp_event_context_deactivate (ec);
633         // we have to keep event_context valid during destruction - otherwise writing
634         // destructors is next to impossible
635         SPEventContext *next = ec->next;
636         sp_event_context_finish (ec);
637         g_object_unref (G_OBJECT (ec));
638         event_context = next;
639     }
641     // The event_context will be null. This means that it will be impossible
642     // to process any event invoked by the lines below. See for example bug
643     // LP #622350. Cutting and undoing again in the node tool resets the event
644     // context to the node tool. In this bug the line bellow invokes GDK_LEAVE_NOTIFY
645     // events which cannot be handled and must be discarded.
646     ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
647     ec->next = event_context;
648     event_context = ec;
649     // Now the event_context has been set again and we can process all events again
650     sp_event_context_activate (ec);
651     _event_context_changed_signal.emit (this, ec);
654 /**
655  * Push event context onto desktop's context stack.
656  */
657 void
658 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
660     SPEventContext *ref, *ec;
662     if (event_context && event_context->key == key) return;
663     ref = event_context;
664     while (ref && ref->next && ref->next->key != key) ref = ref->next;
665     if (ref && ref->next) {
666         ec = ref->next;
667         ref->next = ec->next;
668         sp_event_context_finish (ec);
669         g_object_unref (G_OBJECT (ec));
670     }
672     if (event_context) sp_event_context_deactivate (event_context);
673     ec = sp_event_context_new (type, this, config, key);
674     ec->next = event_context;
675     event_context = ec;
676     sp_event_context_activate (ec);
677     _event_context_changed_signal.emit (this, ec);
680 /**
681  * Sets the coordinate status to a given point
682  */
683 void
684 SPDesktop::set_coordinate_status (Geom::Point p) {
685     _widget->setCoordinateStatus(p);
688 /**
689  * \see SPDocument::getItemFromListAtPointBottom()
690  */
691 SPItem *SPDesktop::getItemFromListAtPointBottom(const GSList *list, Geom::Point const p) const
693     g_return_val_if_fail (doc() != NULL, NULL);
694     return SPDocument::getItemFromListAtPointBottom(dkey, SP_GROUP (doc()->root), list, p);
697 /**
698  * \see SPDocument::getItemAtPoint()
699  */
700 SPItem *SPDesktop::getItemAtPoint(Geom::Point const p, bool into_groups, SPItem *upto) const
702     g_return_val_if_fail (doc() != NULL, NULL);
703     return doc()->getItemAtPoint( dkey, p, into_groups, upto);
706 /**
707  * \see SPDocument::getGroupAtPoint()
708  */
709 SPItem *SPDesktop::getGroupAtPoint(Geom::Point const p) const
711     g_return_val_if_fail (doc() != NULL, NULL);
712     return doc()->getGroupAtPoint(dkey, p);
715 /**
716  * \brief  Returns the mouse point in document coordinates; if mouse is
717  * outside the canvas, returns the center of canvas viewpoint
718  */
719 Geom::Point
720 SPDesktop::point() const
722     Geom::Point p = _widget->getPointer();
723     Geom::Point pw = sp_canvas_window_to_world (canvas, p);
724     p = w2d(pw);
726     Geom::Rect const r = canvas->getViewbox();
728     Geom::Point r0 = w2d(r.min());
729     Geom::Point r1 = w2d(r.max());
731     if (p[Geom::X] >= r0[Geom::X] &&
732         p[Geom::X] <= r1[Geom::X] &&
733         p[Geom::Y] >= r1[Geom::Y] &&
734         p[Geom::Y] <= r0[Geom::Y])
735     {
736         return p;
737     } else {
738         return (r0 + r1) / 2;
739     }
742 /**
743  * Put current zoom data in history list.
744  */
745 void
746 SPDesktop::push_current_zoom (GList **history)
748     Geom::Rect const area = get_display_area();
750     NRRect *old_zoom = g_new(NRRect, 1);
751     old_zoom->x0 = area.min()[Geom::X];
752     old_zoom->x1 = area.max()[Geom::X];
753     old_zoom->y0 = area.min()[Geom::Y];
754     old_zoom->y1 = area.max()[Geom::Y];
755     if ( *history == NULL
756          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
757                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
758                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
759                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
760     {
761         *history = g_list_prepend (*history, old_zoom);
762     }
765 /**
766  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
767  */
768 void
769 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
771     g_assert(_widget);
773     // save the zoom
774     if (log) {
775         push_current_zoom(&zooms_past);
776         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
777         g_list_free (zooms_future);
778         zooms_future = NULL;
779     }
781     double const cx = 0.5 * (x0 + x1);
782     double const cy = 0.5 * (y0 + y1);
784     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
785     Geom::Rect viewbox = canvas->getViewbox();
786     viewbox.expandBy(-border);
788     double scale = _d2w.descrim();
789     double newscale;
790     if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
791         newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
792     } else {
793         newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
794     }
796     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
798     int clear = FALSE;
799     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
800         // zoom changed - set new zoom factors
801         _d2w = Geom::Scale(newscale, -newscale);
802         _w2d = Geom::Scale(1/newscale, 1/-newscale);
803         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
804         clear = TRUE;
805         signal_zoom_changed.emit(_d2w.descrim());
806     }
808     /* Calculate top left corner (in document pixels) */
809     x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
810     y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
812     /* Scroll */
813     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
815     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
816     sp_box3d_context_update_lines(event_context);
818     _widget->updateRulers();
819     _widget->updateScrollbars(_d2w.descrim());
820     _widget->updateZoom();
823 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
825     set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
828 /**
829  * Return viewbox dimensions.
830  */
831 Geom::Rect SPDesktop::get_display_area() const
833     Geom::Rect const viewbox = canvas->getViewbox();
835     double const scale = _d2w[0];
837     return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
838                       Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
841 /**
842  * Revert back to previous zoom if possible.
843  */
844 void
845 SPDesktop::prev_zoom()
847     if (zooms_past == NULL) {
848         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
849         return;
850     }
852     // push current zoom into forward zooms list
853     push_current_zoom (&zooms_future);
855     // restore previous zoom
856     set_display_area (((NRRect *) zooms_past->data)->x0,
857             ((NRRect *) zooms_past->data)->y0,
858             ((NRRect *) zooms_past->data)->x1,
859             ((NRRect *) zooms_past->data)->y1,
860             0, false);
862     // remove the just-added zoom from the past zooms list
863     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
866 /**
867  * Set zoom to next in list.
868  */
869 void
870 SPDesktop::next_zoom()
872     if (zooms_future == NULL) {
873         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
874         return;
875     }
877     // push current zoom into past zooms list
878     push_current_zoom (&zooms_past);
880     // restore next zoom
881     set_display_area (((NRRect *) zooms_future->data)->x0,
882             ((NRRect *) zooms_future->data)->y0,
883             ((NRRect *) zooms_future->data)->x1,
884             ((NRRect *) zooms_future->data)->y1,
885             0, false);
887     // remove the just-used zoom from the zooms_future list
888     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
891 /** \brief  Performs a quick zoom into what the user is working on
892     \param  enable  Whether we're going in or out of quick zoom
894 */
895 void
896 SPDesktop::zoom_quick (bool enable)
898     if (enable == _quick_zoom_enabled) {
899         return;
900     }
902     if (enable == true) {
903         _quick_zoom_stored_area = get_display_area();
904         bool zoomed = false;
906         // TODO This needs to migrate into the node tool, but currently the design
907         // of this method is sufficiently wrong to prevent this.
908         if (!zoomed && INK_IS_NODE_TOOL(event_context)) {
909             InkNodeTool *nt = static_cast<InkNodeTool*>(event_context);
910             if (!nt->_selected_nodes->empty()) {
911                 Geom::Rect nodes = *nt->_selected_nodes->bounds();
912                 double area = nodes.area();
913                 // do not zoom if a single cusp node is selected aand the bounds
914                 // have zero area.
915                 if (!Geom::are_near(area, 0) && area * 2.0 < _quick_zoom_stored_area.area()) {
916                     set_display_area(nodes, true);
917                     zoomed = true;
918                 }
919             }
920         }
922         if (!zoomed) {
923             Geom::OptRect const d = selection->bounds();
924             if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
925                 set_display_area(*d, true);
926                 zoomed = true;
927             }
928         }
930         if (!zoomed) {
931             zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
932             zoomed = true;
933         }
934     } else {
935         set_display_area(_quick_zoom_stored_area, false);
936     }
938     _quick_zoom_enabled = enable;
939     return;
942 /**
943  * Zoom to point with absolute zoom factor.
944  */
945 void
946 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
948     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
950     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
951     // this check prevents "sliding" when trying to zoom in at maximum zoom;
952     /// \todo someone please fix calculations properly and remove this hack
953     if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
954         return;
956     Geom::Rect const viewbox = canvas->getViewbox();
958     double const width2 = viewbox.dimensions()[Geom::X] / zoom;
959     double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
961     set_display_area(cx - px * width2,
962                      cy - py * height2,
963                      cx + (1 - px) * width2,
964                      cy + (1 - py) * height2,
965                      0.0);
968 /**
969   * Apply the desktop's current style or the tool style to the object.
970   */
971 void SPDesktop::applyCurrentOrToolStyle(SPObject *obj, Glib::ustring const &tool_path, bool with_text)
973     SPCSSAttr *css_current = sp_desktop_get_style(this, with_text);
974     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
976     if (prefs->getBool(tool_path + "/usecurrent") && css_current) {
977         obj->setCSS(css_current,"style");
978     } else {
979         SPCSSAttr *css = prefs->getInheritedStyle(tool_path + "/style");
980         obj->setCSS(css,"style");
981         sp_repr_css_attr_unref(css);
982     }
983     if (css_current) {
984         sp_repr_css_attr_unref(css_current);
985     }
988 /**
989  * Zoom to center with absolute zoom factor.
990  */
991 void
992 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
994     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
997 /**
998  * Zoom to point with relative zoom factor.
999  */
1000 void
1001 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
1003     Geom::Rect const area = get_display_area();
1005     if (cx < area.min()[Geom::X]) {
1006         cx = area.min()[Geom::X];
1007     }
1008     if (cx > area.max()[Geom::X]) {
1009         cx = area.max()[Geom::X];
1010     }
1011     if (cy < area.min()[Geom::Y]) {
1012         cy = area.min()[Geom::Y];
1013     }
1014     if (cy > area.max()[Geom::Y]) {
1015         cy = area.max()[Geom::Y];
1016     }
1018     gdouble const scale = _d2w.descrim() * zoom;
1019     double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
1020     double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
1022     zoom_absolute_keep_point(cx, cy, px, py, scale);
1025 /**
1026  * Zoom to center with relative zoom factor.
1027  */
1028 void
1029 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1031     gdouble scale = _d2w.descrim() * zoom;
1032     zoom_absolute (cx, cy, scale);
1035 /**
1036  * Set display area to origin and current document dimensions.
1037  */
1038 void
1039 SPDesktop::zoom_page()
1041     Geom::Rect d(Geom::Point(0, 0),
1042                  Geom::Point(doc()->getWidth(), doc()->getHeight()));
1044     if (d.minExtent() < 1.0) {
1045         return;
1046     }
1048     set_display_area(d, 10);
1051 /**
1052  * Set display area to current document width.
1053  */
1054 void
1055 SPDesktop::zoom_page_width()
1057     Geom::Rect const a = get_display_area();
1059     if (doc()->getWidth() < 1.0) {
1060         return;
1061     }
1063     Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1064                  Geom::Point(doc()->getWidth(), a.midpoint()[Geom::Y]));
1066     set_display_area(d, 10);
1069 /**
1070  * Zoom to selection.
1071  */
1072 void
1073 SPDesktop::zoom_selection()
1075     Geom::OptRect const d = selection->bounds();
1077     if ( !d || d->minExtent() < 0.1 ) {
1078         return;
1079     }
1081     set_display_area(*d, 10);
1084 /**
1085  * Tell widget to let zoom widget grab keyboard focus.
1086  */
1087 void
1088 SPDesktop::zoom_grab_focus()
1090     _widget->letZoomGrabFocus();
1093 /**
1094  * Zoom to whole drawing.
1095  */
1096 void
1097 SPDesktop::zoom_drawing()
1099     g_return_if_fail (doc() != NULL);
1100     SPItem *docitem = SP_ITEM(doc()->getRoot());
1101     g_return_if_fail (docitem != NULL);
1103     Geom::OptRect d = docitem->getBboxDesktop();
1105     /* Note that the second condition here indicates that
1106     ** there are no items in the drawing.
1107     */
1108     if ( !d || d->minExtent() < 0.1 ) {
1109         return;
1110     }
1112     set_display_area(*d, 10);
1115 /**
1116  * Scroll canvas by specific coordinate amount in svg coordinates.
1117  */
1118 void
1119 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1121     double scale = _d2w.descrim();
1122     scroll_world(dx*scale, dy*scale, is_scrolling);
1125 /**
1126  * Scroll canvas by specific coordinate amount.
1127  */
1128 void
1129 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1131     g_assert(_widget);
1133     Geom::Rect const viewbox = canvas->getViewbox();
1135     sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1137     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1138     sp_box3d_context_update_lines(event_context);
1140     _widget->updateRulers();
1141     _widget->updateScrollbars(_d2w.descrim());
1144 bool
1145 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1147     using Geom::X;
1148     using Geom::Y;
1150     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1151     gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1153     // autoscrolldistance is in screen pixels, but the display area is in document units
1154     autoscrolldistance /= _d2w.descrim();
1155     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1156     Geom::Rect dbox = get_display_area();
1157     dbox.expandBy(-autoscrolldistance);
1159     if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1160         !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y])   ) {
1162         Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1164         gdouble x_to;
1165         if (p[X] < dbox.min()[X])
1166             x_to = dbox.min()[X];
1167         else if (p[X] > dbox.max()[X])
1168             x_to = dbox.max()[X];
1169         else
1170             x_to = p[X];
1172         gdouble y_to;
1173         if (p[Y] < dbox.min()[Y])
1174             y_to = dbox.min()[Y];
1175         else if (p[Y] > dbox.max()[Y])
1176             y_to = dbox.max()[Y];
1177         else
1178             y_to = p[Y];
1180         Geom::Point const d_dt(x_to, y_to);
1181         Geom::Point const d_w( d_dt * _d2w );
1182         Geom::Point const moved_w( d_w - s_w );
1184         if (autoscrollspeed == 0)
1185             autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1187         if (autoscrollspeed != 0)
1188             scroll_world (autoscrollspeed * moved_w);
1190         return true;
1191     }
1192     return false;
1195 bool
1196 SPDesktop::is_iconified()
1198     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1201 void
1202 SPDesktop::iconify()
1204     _widget->setIconified();
1207 bool
1208 SPDesktop::is_maximized()
1210     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1213 void
1214 SPDesktop::maximize()
1216     _widget->setMaximized();
1219 bool
1220 SPDesktop::is_fullscreen()
1222     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1225 void
1226 SPDesktop::fullscreen()
1228     _widget->setFullscreen();
1231 /** \brief  Checks to see if the user is working in focused mode
1233     Returns the value of \c _focusMode
1234 */
1235 bool
1236 SPDesktop::is_focusMode()
1238     return _focusMode;
1241 /** \brief  Changes whether the user is in focus mode or not
1242     \param  mode  Which mode the view should be in
1244 */
1245 void
1246 SPDesktop::focusMode (bool mode)
1248     if (mode == _focusMode) { return; }
1250     _focusMode = mode;
1252     layoutWidget();
1253     //sp_desktop_widget_layout(SPDesktopWidget);
1255     return;
1258 void
1259 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1261     _widget->getGeometry (x, y, w, h);
1264 void
1265 SPDesktop::setWindowPosition (Geom::Point p)
1267     _widget->setPosition (p);
1270 void
1271 SPDesktop::setWindowSize (gint w, gint h)
1273     _widget->setSize (w, h);
1276 void
1277 SPDesktop::setWindowTransient (void *p, int transient_policy)
1279     _widget->setTransient (p, transient_policy);
1282 Gtk::Window*
1283 SPDesktop::getToplevel( )
1285     return _widget->getWindow();
1288 void
1289 SPDesktop::presentWindow()
1291     _widget->present();
1294 bool
1295 SPDesktop::warnDialog (gchar *text)
1297     return _widget->warnDialog (text);
1300 void
1301 SPDesktop::toggleRulers()
1303     _widget->toggleRulers();
1306 void
1307 SPDesktop::toggleScrollbars()
1309     _widget->toggleScrollbars();
1312 void
1313 SPDesktop::layoutWidget()
1315     _widget->layout();
1318 void
1319 SPDesktop::destroyWidget()
1321     _widget->destroy();
1324 bool
1325 SPDesktop::shutdown()
1327     return _widget->shutdown();
1330 bool SPDesktop::onDeleteUI (GdkEventAny*)
1332     if(shutdown())
1333         return true;
1335     destroyWidget();
1336     return false;
1339 /**
1340  *  onWindowStateEvent
1341  *
1342  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1343  *  Since GTK doesn't have a way to query this state information directly, we
1344  *  record it for the desktop here, and also possibly trigger a layout.
1345  */
1346 bool
1347 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1349     // Record the desktop window's state
1350     window_state = event->new_window_state;
1352     // Layout may differ depending on full-screen mode or not
1353     GdkWindowState changed = event->changed_mask;
1354     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1355         layoutWidget();
1356     }
1358     return false;
1361 void
1362 SPDesktop::setToolboxFocusTo (gchar const *label)
1364     _widget->setToolboxFocusTo (label);
1367 void
1368 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1370     _widget->setToolboxAdjustmentValue (id, val);
1373 void
1374 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1376     _widget->setToolboxSelectOneValue (id, val);
1379 bool
1380 SPDesktop::isToolboxButtonActive (gchar const *id)
1382     return _widget->isToolboxButtonActive (id);
1385 void
1386 SPDesktop::emitToolSubselectionChanged(gpointer data)
1388     _tool_subselection_changed.emit(data);
1389     inkscape_subselection_changed (this);
1392 void
1393 SPDesktop::updateNow()
1395     sp_canvas_update_now(canvas);
1398 void
1399 SPDesktop::enableInteraction()
1401   _widget->enableInteraction();
1404 void SPDesktop::disableInteraction()
1406   _widget->disableInteraction();
1409 void SPDesktop::setWaitingCursor()
1411     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1412     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1413     gdk_cursor_unref(waiting);
1414     // GDK needs the flush for the cursor change to take effect
1415     gdk_flush();
1416     waiting_cursor = true;
1419 void SPDesktop::clearWaitingCursor()
1421   if (waiting_cursor)
1422       sp_event_context_update_cursor(sp_desktop_event_context(this));
1425 void SPDesktop::toggleColorProfAdjust()
1427     _widget->toggleColorProfAdjust();
1430 void SPDesktop::toggleGrids()
1432     if (namedview->grids) {
1433         if(gridgroup) {
1434             showGrids(!grids_visible);
1435         }
1436     } else {
1437         //there is no grid present at the moment. add a rectangular grid and make it visible
1438         namedview->writeNewGrid(sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1439         showGrids(true);
1440     }
1443 void SPDesktop::showGrids(bool show, bool dirty_document)
1445     grids_visible = show;
1446     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1447     if (show) {
1448         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1449     } else {
1450         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1451     }
1454 void SPDesktop::toggleSnapGlobal()
1456     bool v = namedview->getSnapGlobal();
1457     namedview->setSnapGlobal(!v);
1460 //----------------------------------------------------------------------
1461 // Callback implementations. The virtual ones are connected by the view.
1463 void
1464 SPDesktop::onPositionSet (double x, double y)
1466     _widget->viewSetPosition (Geom::Point(x,y));
1469 void
1470 SPDesktop::onResized (double /*x*/, double /*y*/)
1472    // Nothing called here
1475 /**
1476  * Redraw callback; queues Gtk redraw; connected by View.
1477  */
1478 void
1479 SPDesktop::onRedrawRequested ()
1481     if (main) {
1482         _widget->requestCanvasUpdate();
1483     }
1486 void
1487 SPDesktop::updateCanvasNow()
1489   _widget->requestCanvasUpdateAndWait();
1492 /**
1493  * Associate document with desktop.
1494  */
1495 void
1496 SPDesktop::setDocument (SPDocument *doc)
1498     if (this->doc() && doc) {
1499         namedview->hide(this);
1500         SP_ITEM(this->doc()->getRoot())->invoke_hide(dkey);
1501     }
1503     if (_layer_hierarchy) {
1504         _layer_hierarchy->clear();
1505         delete _layer_hierarchy;
1506     }
1507     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1508     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1509     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1510     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1511     _layer_hierarchy->setTop(doc->getRoot());
1513     /* setup EventLog */
1514     event_log = new Inkscape::EventLog(doc);
1515     doc->addUndoObserver(*event_log);
1517     _commit_connection.disconnect();
1518     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1520     /// \todo fixme: This condition exists to make sure the code
1521     /// inside is NOT called on initialization, only on replacement. But there
1522     /// are surely more safe methods to accomplish this.
1523     // TODO since the comment had reversed logic, check the intent of this block of code:
1524     if (drawing) {
1525         NRArenaItem *ai = 0;
1527         namedview = sp_document_namedview (doc, NULL);
1528         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1529         number = namedview->getViewCount();
1531         ai = SP_ITEM(doc->getRoot())->invoke_show(
1532                 SP_CANVAS_ARENA (drawing)->arena,
1533                 dkey,
1534                 SP_ITEM_SHOW_DISPLAY);
1535         if (ai) {
1536             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1537         }
1538         namedview->show(this);
1539         /* Ugly hack */
1540         activate_guides (true);
1541         /* Ugly hack */
1542         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1543     }
1545     _document_replaced_signal.emit (this, doc);
1547     View::setDocument (doc);
1550 void
1551 SPDesktop::onStatusMessage
1552 (Inkscape::MessageType type, gchar const *message)
1554     if (_widget) {
1555         _widget->setMessage(type, message);
1556     }
1559 void
1560 SPDesktop::onDocumentURISet (gchar const* uri)
1562     _widget->setTitle(uri);
1565 /**
1566  * Resized callback.
1567  */
1568 void
1569 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1571     _doc2dt[5] = height;
1572     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1573     Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1574     SP_CTRLRECT(page)->setRectangle(a);
1575     SP_CTRLRECT(page_border)->setRectangle(a);
1579 void
1580 SPDesktop::_onActivate (SPDesktop* dt)
1582     if (!dt->_widget) return;
1583     dt->_widget->activateDesktop();
1586 void
1587 SPDesktop::_onDeactivate (SPDesktop* dt)
1589     if (!dt->_widget) return;
1590     dt->_widget->deactivateDesktop();
1593 void
1594 SPDesktop::_onSelectionModified
1595 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1597     if (!dt->_widget) return;
1598     dt->_widget->updateScrollbars (dt->_d2w.descrim());
1601 static void
1602 _onSelectionChanged
1603 (Inkscape::Selection *selection, SPDesktop *desktop)
1605     /** \todo
1606      * only change the layer for single selections, or what?
1607      * This seems reasonable -- for multiple selections there can be many
1608      * different layers involved.
1609      */
1610     SPItem *item=selection->singleItem();
1611     if (item) {
1612         SPObject *layer=desktop->layerForObject(item);
1613         if ( layer && layer != desktop->currentLayer() ) {
1614             desktop->setCurrentLayer(layer);
1615         }
1616     }
1619 /**
1620  * Calls event handler of current event context.
1621  * \param arena Unused
1622  * \todo fixme
1623  */
1624 static gint
1625 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1627     if (ai) {
1628         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1629         return sp_event_context_item_handler (desktop->event_context, spi, event);
1630     } else {
1631         return sp_event_context_root_handler (desktop->event_context, event);
1632     }
1635 static void
1636 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1637     g_return_if_fail(SP_IS_GROUP(layer));
1638     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1641 /// Callback
1642 static void
1643 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1644     g_return_if_fail(SP_IS_GROUP(layer));
1645     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1648 /// Callback
1649 static void
1650 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1651                                          SPDesktop *desktop)
1653     desktop->_layer_changed_signal.emit (bottom);
1656 /// Called when document is starting to be rebuilt.
1657 static void
1658 _reconstruction_start (SPDesktop * desktop)
1660     // printf("Desktop, starting reconstruction\n");
1661     desktop->_reconstruction_old_layer_id = g_strdup(desktop->currentLayer()->getId());
1662     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1664     /*
1665     GSList const * selection_objs = desktop->selection->list();
1666     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1668     }
1669     */
1670     desktop->selection->clear();
1672     // printf("Desktop, starting reconstruction end\n");
1675 /// Called when document rebuild is finished.
1676 static void
1677 _reconstruction_finish (SPDesktop * desktop)
1679     // printf("Desktop, finishing reconstruction\n");
1680     if (desktop->_reconstruction_old_layer_id == NULL)
1681         return;
1683     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1684     if (newLayer != NULL)
1685         desktop->setCurrentLayer(newLayer);
1687     g_free(desktop->_reconstruction_old_layer_id);
1688     desktop->_reconstruction_old_layer_id = NULL;
1689     // printf("Desktop, finishing reconstruction end\n");
1690     return;
1693 /**
1694  * Namedview_modified callback.
1695  */
1696 static void
1697 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1699     SPNamedView *nv=SP_NAMEDVIEW(obj);
1701     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1703         /* Show/hide page background */
1704         if (nv->pagecolor & 0xff) {
1705             sp_canvas_item_show (desktop->table);
1706             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1707             sp_canvas_item_move_to_z (desktop->table, 0);
1708         } else {
1709             sp_canvas_item_hide (desktop->table);
1710         }
1712         /* Show/hide page border */
1713         if (nv->showborder) {
1714             // show
1715             sp_canvas_item_show (desktop->page_border);
1716             // set color and shadow
1717             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1718             if (nv->pageshadow) {
1719                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1720             }
1721             // place in the z-order stack
1722             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1723                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1724             } else {
1725                 int order = sp_canvas_item_order (desktop->page_border);
1726                 int morder = sp_canvas_item_order (desktop->drawing);
1727                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1728                     morder - order);
1729             }
1730         } else {
1731                 sp_canvas_item_hide (desktop->page_border);
1732                 if (nv->pageshadow) {
1733                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1734                 }
1735         }
1737         /* Show/hide page shadow */
1738         if (nv->showpageshadow && nv->pageshadow) {
1739             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1740         } else {
1741             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1742         }
1744         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1745         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1746             (SP_RGBA32_R_U(nv->pagecolor) +
1747              SP_RGBA32_G_U(nv->pagecolor) +
1748              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1749             // the background color is light or transparent, use black outline
1750             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1751         } else { // use white outline
1752             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1753         }
1754     }
1757 Geom::Matrix SPDesktop::w2d() const
1759     return _w2d;
1762 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1764     return p * _w2d;
1767 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1769     return p * _d2w;
1772 Geom::Matrix SPDesktop::doc2dt() const
1774     return _doc2dt;
1777 Geom::Matrix SPDesktop::dt2doc() const
1779     // doc2dt is its own inverse
1780     return _doc2dt;
1783 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1785     return p * _doc2dt;
1788 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1790     return p * dt2doc();
1794 /*
1795  * Pop event context from desktop's context stack. Never used.
1796  */
1797 // void
1798 // SPDesktop::pop_event_context (unsigned int key)
1799 // {
1800 //    SPEventContext *ec = NULL;
1801 //
1802 //    if (event_context && event_context->key == key) {
1803 //        g_return_if_fail (event_context);
1804 //        g_return_if_fail (event_context->next);
1805 //        ec = event_context;
1806 //        sp_event_context_deactivate (ec);
1807 //        event_context = ec->next;
1808 //        sp_event_context_activate (event_context);
1809 //        _event_context_changed_signal.emit (this, ec);
1810 //    }
1811 //
1812 //    SPEventContext *ref = event_context;
1813 //    while (ref && ref->next && ref->next->key != key)
1814 //        ref = ref->next;
1815 //
1816 //    if (ref && ref->next) {
1817 //        ec = ref->next;
1818 //        ref->next = ec->next;
1819 //    }
1820 //
1821 //    if (ec) {
1822 //        sp_event_context_finish (ec);
1823 //        g_object_unref (G_OBJECT (ec));
1824 //    }
1825 // }
1827 /*
1828   Local Variables:
1829   mode:c++
1830   c-file-style:"stroustrup"
1831   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1832   indent-tabs-mode:nil
1833   fill-column:99
1834   End:
1835 */
1836 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :