Code

1953ae2a6e8db730bfff77ff61f52a8f398ba6dd
[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 // TODO those includes are only for node tool quick zoom. Remove them after fixing it.
95 #include "ui/tool/node-tool.h"
96 #include "ui/tool/control-point-selection.h"
98 #include "display/sp-canvas.h"
100 namespace Inkscape { namespace XML { class Node; }}
102 // Callback declarations
103 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
104 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
105 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
106 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
107 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
108 static void _reconstruction_start(SPDesktop * desktop);
109 static void _reconstruction_finish(SPDesktop * desktop);
110 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
112 /**
113  * Return new desktop object.
114  * \pre namedview != NULL.
115  * \pre canvas != NULL.
116  */
117 SPDesktop::SPDesktop() :
118     _dlg_mgr( 0 ),
119     namedview( 0 ),
120     canvas( 0 ),
121     selection( 0 ),
122     event_context( 0 ),
123     layer_manager( 0 ),
124     event_log( 0 ),
125     temporary_item_list( 0 ),
126     snapindicator( 0 ),
127     acetate( 0 ),
128     main( 0 ),
129     gridgroup( 0 ),
130     guides( 0 ),
131     drawing( 0 ),
132     sketch( 0 ),
133     controls( 0 ),
134     tempgroup ( 0 ),
135     table( 0 ),
136     page( 0 ),
137     page_border( 0 ),
138     current( 0 ),
139     _focusMode(false),
140     zooms_past( 0 ),
141     zooms_future( 0 ),
142     dkey( 0 ),
143     number( 0 ),
144     window_state(0),
145     interaction_disabled_counter( 0 ),
146     waiting_cursor( false ),
147     guides_active( false ),
148     gr_item( 0 ),
149     gr_point_type( 0 ),
150     gr_point_i( 0 ),
151     gr_fill_or_stroke( true ),
152     _layer_hierarchy( 0 ),
153     _reconstruction_old_layer_id( 0 ),
154     _display_mode(Inkscape::RENDERMODE_NORMAL),
155     _widget( 0 ),
156     _inkscape( 0 ),
157     _guides_message_context( 0 ),
158     _active( false ),
159     _w2d(),
160     _d2w(),
161     _doc2dt( Geom::identity() ),
162     grids_visible( false )
164     _d2w.setIdentity();
165     _w2d.setIdentity();
167     selection = Inkscape::GC::release( new Inkscape::Selection(this) );
170 void
171 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas, Inkscape::UI::View::EditWidgetInterface *widget)
173     _widget = widget;
175     // Temporary workaround for link order issues:
176     Inkscape::DeviceManager::getManager().getDevices();
177     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
179     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
181     current = prefs->getStyle("/desktop/style");
183     namedview = nv;
184     canvas = aCanvas;
186     SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
187     /* Kill flicker */
188     sp_document_ensure_up_to_date (document);
190     /* Setup Dialog Manager */
191     _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
193     dkey = sp_item_display_key_new (1);
195     /* Connect document */
196     setDocument (document);
198     number = namedview->getViewCount();
201     /* Setup Canvas */
202     g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
204     SPCanvasGroup *root = sp_canvas_root (canvas);
206     /* Setup adminstrative layers */
207     acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
208     g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
209     main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
210     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
212     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
213     SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
214     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
215     sp_canvas_item_move_to_z (table, 0);
217     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
218     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
219     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
221     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
222     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
224     SP_CANVAS_ARENA (drawing)->arena->delta = prefs->getDouble("/options/cursortolerance/value", 1.0); // default is 1 px
226     if (prefs->getBool("/options/startmode/outline")) {
227         // Start in outline mode
228         setDisplayModeOutline();
229     } else {
230         // Start in normal mode, default
231         setDisplayModeNormal();
232     }
234     // The order in which these canvas items are added determines the z-order. It's therefore
235     // important to add the tempgroup (which will contain the snapindicator) before adding the
236     // controls. Only this way one will be able to quickly (before the snap indicator has
237     // disappeared) reselect a node after snapping it. If the z-order is wrong however, this
238     // will not work (the snap indicator is on top of the node handler; is the snapindicator
239     // being selected? or does it intercept some of the events that should have gone to the
240     // node handler? see bug https://bugs.launchpad.net/inkscape/+bug/414142)
241     gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
242     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
243     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
244     tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
245     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
247     /* Push select tool to the bottom of stack */
248     /** \todo
249      * FIXME: this is the only call to this.  Everything else seems to just
250      * call "set" instead of "push".  Can we assume that there is only one
251      * context ever?
252      */
253     push_event_context (SP_TYPE_SELECT_CONTEXT, "/tools/select", SP_EVENT_CONTEXT_STATIC);
255     // display rect and zoom are now handled in sp_desktop_widget_realize()
257     Geom::Rect const d(Geom::Point(0.0, 0.0),
258                        Geom::Point(sp_document_width(document), sp_document_height(document)));
260     SP_CTRLRECT(page)->setRectangle(d);
261     SP_CTRLRECT(page_border)->setRectangle(d);
263     /* the following sets the page shadow on the canvas
264        It was originally set to 5, which is really cheesy!
265        It now is an attribute in the document's namedview. If a value of
266        0 is used, then the constructor for a shadow is not initialized.
267     */
269     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
270         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
271     }
274     /* Connect event for page resize */
275     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
277     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
279     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
280             SP_CANVAS_ARENA (drawing)->arena,
281             dkey,
282             SP_ITEM_SHOW_DISPLAY);
283     if (ai) {
284         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
285     }
287     namedview->show(this);
288     /* Ugly hack */
289     activate_guides (true);
290     /* Ugly hack */
291     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
293 /* Set up notification of rebuilding the document, this allows
294        for saving object related settings in the document. */
295     _reconstruction_start_connection =
296         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
297     _reconstruction_finish_connection =
298         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
299     _reconstruction_old_layer_id = NULL;
301     // ?
302     // sp_active_desktop_set (desktop);
303     _inkscape = INKSCAPE;
305     _activate_connection = _activate_signal.connect(
306         sigc::bind(
307             sigc::ptr_fun(_onActivate),
308             this
309         )
310     );
311      _deactivate_connection = _deactivate_signal.connect(
312         sigc::bind(
313             sigc::ptr_fun(_onDeactivate),
314             this
315         )
316     );
318     _sel_modified_connection = selection->connectModified(
319         sigc::bind(
320             sigc::ptr_fun(&_onSelectionModified),
321             this
322         )
323     );
324     _sel_changed_connection = selection->connectChanged(
325         sigc::bind(
326             sigc::ptr_fun(&_onSelectionChanged),
327             this
328         )
329     );
332     /* setup LayerManager */
333     //   (Setting up after the connections are all in place, as it may use some of them)
334     layer_manager = new Inkscape::LayerManager( this );
336     showGrids(namedview->grids_visible, false);
338     temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
339     snapindicator = new Inkscape::Display::SnapIndicator ( this );
343 void SPDesktop::destroy()
345     if (snapindicator) {
346         delete snapindicator;
347         snapindicator = NULL;
348     }
349     if (temporary_item_list) {
350         delete temporary_item_list;
351         temporary_item_list = NULL;
352     }
354     if (selection) {
355         delete selection;
356         selection = NULL;
357     }
359     namedview->hide(this);
361     _activate_connection.disconnect();
362     _deactivate_connection.disconnect();
363     _sel_modified_connection.disconnect();
364     _sel_changed_connection.disconnect();
365     _modified_connection.disconnect();
366     _commit_connection.disconnect();
367     _reconstruction_start_connection.disconnect();
368     _reconstruction_finish_connection.disconnect();
370     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
371     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
372     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
374     while (event_context) {
375         SPEventContext *ec = event_context;
376         event_context = ec->next;
377         sp_event_context_finish (ec);
378         g_object_unref (G_OBJECT (ec));
379     }
381     if (_layer_hierarchy) {
382         delete _layer_hierarchy;
383 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
384     }
386     if (layer_manager) {
387         delete layer_manager;
388         layer_manager = NULL;
389     }
391     if (_inkscape) {
392         _inkscape = NULL;
393     }
395     if (drawing) {
396         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
397         drawing = NULL;
398     }
400     delete _guides_message_context;
401     _guides_message_context = NULL;
403     g_list_free (zooms_past);
404     g_list_free (zooms_future);
407 SPDesktop::~SPDesktop() {}
409 //--------------------------------------------------------------------
410 /* Public methods */
413 /* These methods help for temporarily showing things on-canvas.
414  * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
415  * is when you want to prematurely remove the item from the canvas, by calling
416  * desktop->remove_temporary_canvasitem(tempitem).
417  */
418 /** Note that lifetime is measured in milliseconds
419  * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
420  * delete the object for you and the reference will become invalid without you knowing it.
421  * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
422  * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
423  * because the object might be deleted already without you knowing it.
424  * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
425  */
426 Inkscape::Display::TemporaryItem *
427 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
429     if (move_to_bottom) {
430         sp_canvas_item_move_to_z(item, 0);
431     }
433     return temporary_item_list->add_item(item, lifetime);
436 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
437 */
438 void
439 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
441     // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
442     if (tempitem && temporary_item_list) {
443         temporary_item_list->delete_item(tempitem);
444     }
447 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
448     SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
449     canvas->rendermode = mode;
450     _display_mode = mode;
451     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
452     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
455 void SPDesktop::displayModeToggle() {
456     switch (_display_mode) {
457     case Inkscape::RENDERMODE_NORMAL:
458         _setDisplayMode(Inkscape::RENDERMODE_NO_FILTERS);
459         break;
460     case Inkscape::RENDERMODE_NO_FILTERS:
461         _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
462         break;
463     case Inkscape::RENDERMODE_OUTLINE:
464         _setDisplayMode(Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW);
465         break;
466     case Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW:
467     default:
468         _setDisplayMode(Inkscape::RENDERMODE_NORMAL);
469     }
472 /**
473  * Returns current root (=bottom) layer.
474  */
475 SPObject *SPDesktop::currentRoot() const
477     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
480 /**
481  * Returns current top layer.
482  */
483 SPObject *SPDesktop::currentLayer() const
485     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
488 /**
489  * Sets the current layer of the desktop.
490  *
491  * Make \a object the top layer.
492  */
493 void SPDesktop::setCurrentLayer(SPObject *object) {
494     g_return_if_fail(SP_IS_GROUP(object));
495     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
496     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
497     _layer_hierarchy->setBottom(object);
500 void SPDesktop::toggleLayerSolo(SPObject *object) {
501     g_return_if_fail(SP_IS_GROUP(object));
502     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
504     bool othersShowing = false;
505     std::vector<SPObject*> layers;
506     for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
507         layers.push_back(obj);
508         othersShowing |= !SP_ITEM(obj)->isHidden();
509     }
510     for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
511         layers.push_back(obj);
512         othersShowing |= !SP_ITEM(obj)->isHidden();
513     }
516     if ( SP_ITEM(object)->isHidden() ) {
517         SP_ITEM(object)->setHidden(false);
518     }
520     for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
521         SP_ITEM(*it)->setHidden(othersShowing);
522     }
525 /**
526  * Return layer that contains \a object.
527  */
528 SPObject *SPDesktop::layerForObject(SPObject *object) {
529     g_return_val_if_fail(object != NULL, NULL);
531     SPObject *root=currentRoot();
532     object = SP_OBJECT_PARENT(object);
533     while ( object && object != root && !isLayer(object) ) {
534         object = SP_OBJECT_PARENT(object);
535     }
536     return object;
539 /**
540  * True if object is a layer.
541  */
542 bool SPDesktop::isLayer(SPObject *object) const {
543     return ( SP_IS_GROUP(object)
544              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
545                   == SPGroup::LAYER ) );
548 /**
549  * True if desktop viewport fully contains \a item's bbox.
550  */
551 bool SPDesktop::isWithinViewport (SPItem *item) const
553     Geom::Rect const viewport = get_display_area();
554     Geom::OptRect const bbox = sp_item_bbox_desktop(item);
555     if (bbox) {
556         return viewport.contains(*bbox);
557     } else {
558         return true;
559     }
562 ///
563 bool SPDesktop::itemIsHidden(SPItem const *item) const {
564     return item->isHidden(this->dkey);
567 /**
568  * Set activate property of desktop; emit signal if changed.
569  */
570 void
571 SPDesktop::set_active (bool new_active)
573     if (new_active != _active) {
574         _active = new_active;
575         if (new_active) {
576             _activate_signal.emit();
577         } else {
578             _deactivate_signal.emit();
579         }
580     }
583 /**
584  * Set activate status of current desktop's named view.
585  */
586 void
587 SPDesktop::activate_guides(bool activate)
589     guides_active = activate;
590     namedview->activateGuides(this, activate);
593 /**
594  * Make desktop switch documents.
595  */
596 void
597 SPDesktop::change_document (SPDocument *theDocument)
599     g_return_if_fail (theDocument != NULL);
601     /* unselect everything before switching documents */
602     selection->clear();
604     setDocument (theDocument);
606     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
607        (this can probably be done in a better way) */
608     Gtk::Window *parent = this->getToplevel();
609     g_assert(parent != NULL);
610     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
611     if (dtw) {
612         dtw->desktop = this;
613     }
614     dtw->updateNamedview();
616     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
617     _document_replaced_signal.emit (this, theDocument);
620 /**
621  * Make desktop switch event contexts.
622  */
623 void
624 SPDesktop::set_event_context (GtkType type, const gchar *config)
626     SPEventContext *ec;
627     while (event_context) {
628         ec = event_context;
629         sp_event_context_deactivate (ec);
630         // we have to keep event_context valid during destruction - otherwise writing
631         // destructors is next to impossible
632         SPEventContext *next = ec->next;
633         sp_event_context_finish (ec);
634         g_object_unref (G_OBJECT (ec));
635         event_context = next;
636     }
638     ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
639     ec->next = event_context;
640     event_context = ec;
641     sp_event_context_activate (ec);
642     _event_context_changed_signal.emit (this, ec);
645 /**
646  * Push event context onto desktop's context stack.
647  */
648 void
649 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
651     SPEventContext *ref, *ec;
653     if (event_context && event_context->key == key) return;
654     ref = event_context;
655     while (ref && ref->next && ref->next->key != key) ref = ref->next;
656     if (ref && ref->next) {
657         ec = ref->next;
658         ref->next = ec->next;
659         sp_event_context_finish (ec);
660         g_object_unref (G_OBJECT (ec));
661     }
663     if (event_context) sp_event_context_deactivate (event_context);
664     ec = sp_event_context_new (type, this, config, key);
665     ec->next = event_context;
666     event_context = ec;
667     sp_event_context_activate (ec);
668     _event_context_changed_signal.emit (this, ec);
671 /**
672  * Sets the coordinate status to a given point
673  */
674 void
675 SPDesktop::set_coordinate_status (Geom::Point p) {
676     _widget->setCoordinateStatus(p);
679 /**
680  * \see sp_document_item_from_list_at_point_bottom()
681  */
682 SPItem *
683 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
685     g_return_val_if_fail (doc() != NULL, NULL);
686     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
689 /**
690  * \see sp_document_item_at_point()
691  */
692 SPItem *
693 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
695     g_return_val_if_fail (doc() != NULL, NULL);
696     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
699 /**
700  * \see sp_document_group_at_point()
701  */
702 SPItem *
703 SPDesktop::group_at_point (Geom::Point const p) const
705     g_return_val_if_fail (doc() != NULL, NULL);
706     return sp_document_group_at_point (doc(), dkey, p);
709 /**
710  * \brief  Returns the mouse point in document coordinates; if mouse is
711  * outside the canvas, returns the center of canvas viewpoint
712  */
713 Geom::Point
714 SPDesktop::point() const
716     Geom::Point p = _widget->getPointer();
717     Geom::Point pw = sp_canvas_window_to_world (canvas, p);
718     p = w2d(pw);
720     Geom::Rect const r = canvas->getViewbox();
722     Geom::Point r0 = w2d(r.min());
723     Geom::Point r1 = w2d(r.max());
725     if (p[Geom::X] >= r0[Geom::X] &&
726         p[Geom::X] <= r1[Geom::X] &&
727         p[Geom::Y] >= r1[Geom::Y] &&
728         p[Geom::Y] <= r0[Geom::Y])
729     {
730         return p;
731     } else {
732         return (r0 + r1) / 2;
733     }
736 /**
737  * Put current zoom data in history list.
738  */
739 void
740 SPDesktop::push_current_zoom (GList **history)
742     Geom::Rect const area = get_display_area();
744     NRRect *old_zoom = g_new(NRRect, 1);
745     old_zoom->x0 = area.min()[Geom::X];
746     old_zoom->x1 = area.max()[Geom::X];
747     old_zoom->y0 = area.min()[Geom::Y];
748     old_zoom->y1 = area.max()[Geom::Y];
749     if ( *history == NULL
750          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
751                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
752                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
753                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
754     {
755         *history = g_list_prepend (*history, old_zoom);
756     }
759 /**
760  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
761  */
762 void
763 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
765     g_assert(_widget);
767     // save the zoom
768     if (log) {
769         push_current_zoom(&zooms_past);
770         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
771         g_list_free (zooms_future);
772         zooms_future = NULL;
773     }
775     double const cx = 0.5 * (x0 + x1);
776     double const cy = 0.5 * (y0 + y1);
778     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
779     Geom::Rect viewbox = canvas->getViewbox();
780     viewbox.expandBy(-border);
782     double scale = _d2w.descrim();
783     double newscale;
784     if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
785         newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
786     } else {
787         newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
788     }
790     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
792     int clear = FALSE;
793     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
794         // zoom changed - set new zoom factors
795         _d2w = Geom::Scale(newscale);
796         _w2d = Geom::Scale(1/newscale);
797         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
798         clear = TRUE;
799         signal_zoom_changed.emit(_d2w.descrim());
800     }
802     /* Calculate top left corner (in document pixels) */
803     x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
804     y0 = cy - 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
806     /* Scroll */
807     sp_canvas_scroll_to (canvas, x0 * newscale - border, y0 * newscale - border, clear);
809     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
810     sp_box3d_context_update_lines(event_context);
812     _widget->updateRulers();
813     _widget->updateScrollbars(_d2w.descrim());
814     _widget->updateZoom();
817 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
819     set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
822 /**
823  * Return viewbox dimensions.
824  */
825 Geom::Rect SPDesktop::get_display_area() const
827     Geom::Rect const viewbox = canvas->getViewbox();
829     double const scale = _d2w[0];
831     return viewbox * (1./scale);
834 /**
835  * Revert back to previous zoom if possible.
836  */
837 void
838 SPDesktop::prev_zoom()
840     if (zooms_past == NULL) {
841         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
842         return;
843     }
845     // push current zoom into forward zooms list
846     push_current_zoom (&zooms_future);
848     // restore previous zoom
849     set_display_area (((NRRect *) zooms_past->data)->x0,
850             ((NRRect *) zooms_past->data)->y0,
851             ((NRRect *) zooms_past->data)->x1,
852             ((NRRect *) zooms_past->data)->y1,
853             0, false);
855     // remove the just-added zoom from the past zooms list
856     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
859 /**
860  * Set zoom to next in list.
861  */
862 void
863 SPDesktop::next_zoom()
865     if (zooms_future == NULL) {
866         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
867         return;
868     }
870     // push current zoom into past zooms list
871     push_current_zoom (&zooms_past);
873     // restore next zoom
874     set_display_area (((NRRect *) zooms_future->data)->x0,
875             ((NRRect *) zooms_future->data)->y0,
876             ((NRRect *) zooms_future->data)->x1,
877             ((NRRect *) zooms_future->data)->y1,
878             0, false);
880     // remove the just-used zoom from the zooms_future list
881     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
884 /** \brief  Performs a quick zoom into what the user is working on
885     \param  enable  Whether we're going in or out of quick zoom
887 */
888 void
889 SPDesktop::zoom_quick (bool enable)
891     if (enable == _quick_zoom_enabled) {
892         return;
893     }
895     if (enable == true) {
896         _quick_zoom_stored_area = get_display_area();
897         bool zoomed = false;
899         // TODO This needs to migrate into the node tool, but currently the design
900         // of this method is sufficiently wrong to prevent this.
901         if (!zoomed && INK_IS_NODE_TOOL(event_context)) {
902             InkNodeTool *nt = static_cast<InkNodeTool*>(event_context);
903             if (!nt->_selected_nodes->empty()) {
904                 Geom::Rect nodes = *nt->_selected_nodes->bounds();
905                 double area = nodes.area();
906                 // do not zoom if a single cusp node is selected aand the bounds
907                 // have zero area.
908                 if (!Geom::are_near(area, 0) && area * 2.0 < _quick_zoom_stored_area.area()) {
909                     set_display_area(nodes, true);
910                     zoomed = true;
911                 }
912             }
913         }
915         if (!zoomed) {
916             Geom::OptRect const d = selection->bounds();
917             if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
918                 set_display_area(*d, true);
919                 zoomed = true;
920             }
921         }
923         if (!zoomed) {
924             zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
925             zoomed = true;
926         }
927     } else {
928         set_display_area(_quick_zoom_stored_area, false);
929     }
931     _quick_zoom_enabled = enable;
932     return;
935 /**
936  * Zoom to point with absolute zoom factor.
937  */
938 void
939 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
941     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
943     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
944     // this check prevents "sliding" when trying to zoom in at maximum zoom;
945     /// \todo someone please fix calculations properly and remove this hack
946     if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
947         return;
949     Geom::Rect const viewbox = canvas->getViewbox();
951     double const width2 = viewbox.dimensions()[Geom::X] / zoom;
952     double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
954     set_display_area(cx - px * width2,
955                      cy - py * height2,
956                      cx + (1 - px) * width2,
957                      cy + (1 - py) * height2,
958                      0.0);
961 /**
962  * Zoom to center with absolute zoom factor.
963  */
964 void
965 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
967     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
970 /**
971  * Zoom to point with relative zoom factor.
972  */
973 void
974 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
976     Geom::Rect const area = get_display_area();
978     if (cx < area.min()[Geom::X]) {
979         cx = area.min()[Geom::X];
980     }
981     if (cx > area.max()[Geom::X]) {
982         cx = area.max()[Geom::X];
983     }
984     if (cy < area.min()[Geom::Y]) {
985         cy = area.min()[Geom::Y];
986     }
987     if (cy > area.max()[Geom::Y]) {
988         cy = area.max()[Geom::Y];
989     }
991     gdouble const scale = _d2w.descrim() * zoom;
992     double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
993     double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
995     zoom_absolute_keep_point(cx, cy, px, py, scale);
998 /**
999  * Zoom to center with relative zoom factor.
1000  */
1001 void
1002 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1004     gdouble scale = _d2w.descrim() * zoom;
1005     zoom_absolute (cx, cy, scale);
1008 /**
1009  * Set display area to origin and current document dimensions.
1010  */
1011 void
1012 SPDesktop::zoom_page()
1014     Geom::Rect d(Geom::Point(0, 0),
1015                  Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1017     if (d.minExtent() < 1.0) {
1018         return;
1019     }
1021     set_display_area(d, 10);
1024 /**
1025  * Set display area to current document width.
1026  */
1027 void
1028 SPDesktop::zoom_page_width()
1030     Geom::Rect const a = get_display_area();
1032     if (sp_document_width(doc()) < 1.0) {
1033         return;
1034     }
1036     Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1037                  Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1039     set_display_area(d, 10);
1042 /**
1043  * Zoom to selection.
1044  */
1045 void
1046 SPDesktop::zoom_selection()
1048     Geom::OptRect const d = selection->bounds();
1050     if ( !d || d->minExtent() < 0.1 ) {
1051         return;
1052     }
1054     set_display_area(*d, 10);
1057 /**
1058  * Tell widget to let zoom widget grab keyboard focus.
1059  */
1060 void
1061 SPDesktop::zoom_grab_focus()
1063     _widget->letZoomGrabFocus();
1066 /**
1067  * Zoom to whole drawing.
1068  */
1069 void
1070 SPDesktop::zoom_drawing()
1072     g_return_if_fail (doc() != NULL);
1073     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1074     g_return_if_fail (docitem != NULL);
1076     Geom::OptRect d = sp_item_bbox_desktop(docitem);
1078     /* Note that the second condition here indicates that
1079     ** there are no items in the drawing.
1080     */
1081     if ( !d || d->minExtent() < 0.1 ) {
1082         return;
1083     }
1085     set_display_area(*d, 10);
1088 /**
1089  * Scroll canvas by specific coordinate amount in svg coordinates.
1090  */
1091 void
1092 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1094     double scale = _d2w.descrim();
1095     scroll_world(dx*scale, dy*scale, is_scrolling);
1098 /**
1099  * Scroll canvas by specific coordinate amount.
1100  */
1101 void
1102 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1104     g_assert(_widget);
1106     Geom::Rect const viewbox = canvas->getViewbox();
1108     sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1110     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1111     sp_box3d_context_update_lines(event_context);
1113     _widget->updateRulers();
1114     _widget->updateScrollbars(_d2w.descrim());
1117 bool
1118 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1120     using Geom::X;
1121     using Geom::Y;
1123     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1124     gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1126     // autoscrolldistance is in screen pixels, but the display area is in document units
1127     autoscrolldistance /= _d2w.descrim();
1128     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1129     Geom::Rect dbox = get_display_area();
1130     dbox.expandBy(-autoscrolldistance);
1132     if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1133         !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y])   ) {
1135         Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1137         gdouble x_to;
1138         if (p[X] < dbox.min()[X])
1139             x_to = dbox.min()[X];
1140         else if (p[X] > dbox.max()[X])
1141             x_to = dbox.max()[X];
1142         else
1143             x_to = p[X];
1145         gdouble y_to;
1146         if (p[Y] < dbox.min()[Y])
1147             y_to = dbox.min()[Y];
1148         else if (p[Y] > dbox.max()[Y])
1149             y_to = dbox.max()[Y];
1150         else
1151             y_to = p[Y];
1153         Geom::Point const d_dt(x_to, y_to);
1154         Geom::Point const d_w( d_dt * _d2w );
1155         Geom::Point const moved_w( d_w - s_w );
1157         if (autoscrollspeed == 0)
1158             autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1160         if (autoscrollspeed != 0)
1161             scroll_world (autoscrollspeed * moved_w);
1163         return true;
1164     }
1165     return false;
1168 bool
1169 SPDesktop::is_iconified()
1171     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1174 void
1175 SPDesktop::iconify()
1177     _widget->setIconified();
1180 bool
1181 SPDesktop::is_maximized()
1183     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1186 void
1187 SPDesktop::maximize()
1189     _widget->setMaximized();
1192 bool
1193 SPDesktop::is_fullscreen()
1195     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1198 void
1199 SPDesktop::fullscreen()
1201     _widget->setFullscreen();
1204 /** \brief  Checks to see if the user is working in focused mode
1206     Returns the value of \c _focusMode
1207 */
1208 bool
1209 SPDesktop::is_focusMode()
1211     return _focusMode;
1214 /** \brief  Changes whether the user is in focus mode or not
1215     \param  mode  Which mode the view should be in
1217 */
1218 void
1219 SPDesktop::focusMode (bool mode)
1221     if (mode == _focusMode) { return; }
1223     _focusMode = mode;
1225     layoutWidget();
1226     //sp_desktop_widget_layout(SPDesktopWidget);
1228     return;
1231 void
1232 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1234     _widget->getGeometry (x, y, w, h);
1237 void
1238 SPDesktop::setWindowPosition (Geom::Point p)
1240     _widget->setPosition (p);
1243 void
1244 SPDesktop::setWindowSize (gint w, gint h)
1246     _widget->setSize (w, h);
1249 void
1250 SPDesktop::setWindowTransient (void *p, int transient_policy)
1252     _widget->setTransient (p, transient_policy);
1255 Gtk::Window*
1256 SPDesktop::getToplevel( )
1258     return _widget->getWindow();
1261 void
1262 SPDesktop::presentWindow()
1264     _widget->present();
1267 bool
1268 SPDesktop::warnDialog (gchar *text)
1270     return _widget->warnDialog (text);
1273 void
1274 SPDesktop::toggleRulers()
1276     _widget->toggleRulers();
1279 void
1280 SPDesktop::toggleScrollbars()
1282     _widget->toggleScrollbars();
1285 void
1286 SPDesktop::layoutWidget()
1288     _widget->layout();
1291 void
1292 SPDesktop::destroyWidget()
1294     _widget->destroy();
1297 bool
1298 SPDesktop::shutdown()
1300     return _widget->shutdown();
1303 bool SPDesktop::onDeleteUI (GdkEventAny*)
1305     if(shutdown())
1306         return true;
1308     destroyWidget();
1309     return false;
1312 /**
1313  *  onWindowStateEvent
1314  *
1315  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1316  *  Since GTK doesn't have a way to query this state information directly, we
1317  *  record it for the desktop here, and also possibly trigger a layout.
1318  */
1319 bool
1320 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1322     // Record the desktop window's state
1323     window_state = event->new_window_state;
1325     // Layout may differ depending on full-screen mode or not
1326     GdkWindowState changed = event->changed_mask;
1327     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1328         layoutWidget();
1329     }
1331     return false;
1334 void
1335 SPDesktop::setToolboxFocusTo (gchar const *label)
1337     _widget->setToolboxFocusTo (label);
1340 void
1341 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1343     _widget->setToolboxAdjustmentValue (id, val);
1346 void
1347 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1349     _widget->setToolboxSelectOneValue (id, val);
1352 bool
1353 SPDesktop::isToolboxButtonActive (gchar const *id)
1355     return _widget->isToolboxButtonActive (id);
1358 void
1359 SPDesktop::emitToolSubselectionChanged(gpointer data)
1361     _tool_subselection_changed.emit(data);
1362     inkscape_subselection_changed (this);
1365 void
1366 SPDesktop::updateNow()
1368   sp_canvas_update_now(canvas);
1371 void
1372 SPDesktop::enableInteraction()
1374   _widget->enableInteraction();
1377 void SPDesktop::disableInteraction()
1379   _widget->disableInteraction();
1382 void SPDesktop::setWaitingCursor()
1384     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1385     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1386     gdk_cursor_unref(waiting);
1387     // GDK needs the flush for the cursor change to take effect
1388     gdk_flush();
1389     waiting_cursor = true;
1392 void SPDesktop::clearWaitingCursor()
1394   if (waiting_cursor)
1395       sp_event_context_update_cursor(sp_desktop_event_context(this));
1398 void SPDesktop::toggleColorProfAdjust()
1400     _widget->toggleColorProfAdjust();
1403 void SPDesktop::toggleGrids()
1405     if (namedview->grids) {
1406         if(gridgroup) {
1407             showGrids(!grids_visible);
1408         }
1409     } else {
1410         //there is no grid present at the moment. add a rectangular grid and make it visible
1411         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1412         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1413         showGrids(true);
1414     }
1417 void SPDesktop::showGrids(bool show, bool dirty_document)
1419     grids_visible = show;
1420     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1421     if (show) {
1422         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1423     } else {
1424         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1425     }
1428 void SPDesktop::toggleSnapGlobal()
1430     bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1431     Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1432     sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1435 //----------------------------------------------------------------------
1436 // Callback implementations. The virtual ones are connected by the view.
1438 void
1439 SPDesktop::onPositionSet (double x, double y)
1441     _widget->viewSetPosition (Geom::Point(x,y));
1444 void
1445 SPDesktop::onResized (double /*x*/, double /*y*/)
1447    // Nothing called here
1450 /**
1451  * Redraw callback; queues Gtk redraw; connected by View.
1452  */
1453 void
1454 SPDesktop::onRedrawRequested ()
1456     if (main) {
1457         _widget->requestCanvasUpdate();
1458     }
1461 void
1462 SPDesktop::updateCanvasNow()
1464   _widget->requestCanvasUpdateAndWait();
1467 /**
1468  * Associate document with desktop.
1469  */
1470 void
1471 SPDesktop::setDocument (SPDocument *doc)
1473     if (this->doc() && doc) {
1474         namedview->hide(this);
1475         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1476     }
1478     if (_layer_hierarchy) {
1479         _layer_hierarchy->clear();
1480         delete _layer_hierarchy;
1481     }
1482     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1483     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1484     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1485     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1486     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1488     /* setup EventLog */
1489     event_log = new Inkscape::EventLog(doc);
1490     doc->addUndoObserver(*event_log);
1492     _commit_connection.disconnect();
1493     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1495     /// \todo fixme: This condition exists to make sure the code
1496     /// inside is NOT called on initialization, only on replacement. But there
1497     /// are surely more safe methods to accomplish this.
1498     // TODO since the comment had reversed logic, check the intent of this block of code:
1499     if (drawing) {
1500         NRArenaItem *ai = 0;
1502         namedview = sp_document_namedview (doc, NULL);
1503         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1504         number = namedview->getViewCount();
1506         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1507                 SP_CANVAS_ARENA (drawing)->arena,
1508                 dkey,
1509                 SP_ITEM_SHOW_DISPLAY);
1510         if (ai) {
1511             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1512         }
1513         namedview->show(this);
1514         /* Ugly hack */
1515         activate_guides (true);
1516         /* Ugly hack */
1517         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1518     }
1520     _document_replaced_signal.emit (this, doc);
1522     View::setDocument (doc);
1525 void
1526 SPDesktop::onStatusMessage
1527 (Inkscape::MessageType type, gchar const *message)
1529     if (_widget) {
1530         _widget->setMessage(type, message);
1531     }
1534 void
1535 SPDesktop::onDocumentURISet (gchar const* uri)
1537     _widget->setTitle(uri);
1540 /**
1541  * Resized callback.
1542  */
1543 void
1544 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1546     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1547     Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1548     SP_CTRLRECT(page)->setRectangle(a);
1549     SP_CTRLRECT(page_border)->setRectangle(a);
1553 void
1554 SPDesktop::_onActivate (SPDesktop* dt)
1556     if (!dt->_widget) return;
1557     dt->_widget->activateDesktop();
1560 void
1561 SPDesktop::_onDeactivate (SPDesktop* dt)
1563     if (!dt->_widget) return;
1564     dt->_widget->deactivateDesktop();
1567 void
1568 SPDesktop::_onSelectionModified
1569 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1571     if (!dt->_widget) return;
1572     dt->_widget->updateScrollbars (dt->_d2w.descrim());
1575 static void
1576 _onSelectionChanged
1577 (Inkscape::Selection *selection, SPDesktop *desktop)
1579     /** \todo
1580      * only change the layer for single selections, or what?
1581      * This seems reasonable -- for multiple selections there can be many
1582      * different layers involved.
1583      */
1584     SPItem *item=selection->singleItem();
1585     if (item) {
1586         SPObject *layer=desktop->layerForObject(item);
1587         if ( layer && layer != desktop->currentLayer() ) {
1588             desktop->setCurrentLayer(layer);
1589         }
1590     }
1593 /**
1594  * Calls event handler of current event context.
1595  * \param arena Unused
1596  * \todo fixme
1597  */
1598 static gint
1599 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1601     if (ai) {
1602         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1603         return sp_event_context_item_handler (desktop->event_context, spi, event);
1604     } else {
1605         return sp_event_context_root_handler (desktop->event_context, event);
1606     }
1609 static void
1610 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1611     g_return_if_fail(SP_IS_GROUP(layer));
1612     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1615 /// Callback
1616 static void
1617 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1618     g_return_if_fail(SP_IS_GROUP(layer));
1619     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1622 /// Callback
1623 static void
1624 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1625                                          SPDesktop *desktop)
1627     desktop->_layer_changed_signal.emit (bottom);
1630 /// Called when document is starting to be rebuilt.
1631 static void
1632 _reconstruction_start (SPDesktop * desktop)
1634     // printf("Desktop, starting reconstruction\n");
1635     desktop->_reconstruction_old_layer_id = g_strdup(desktop->currentLayer()->getId());
1636     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1638     /*
1639     GSList const * selection_objs = desktop->selection->list();
1640     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1642     }
1643     */
1644     desktop->selection->clear();
1646     // printf("Desktop, starting reconstruction end\n");
1649 /// Called when document rebuild is finished.
1650 static void
1651 _reconstruction_finish (SPDesktop * desktop)
1653     // printf("Desktop, finishing reconstruction\n");
1654     if (desktop->_reconstruction_old_layer_id == NULL)
1655         return;
1657     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1658     if (newLayer != NULL)
1659         desktop->setCurrentLayer(newLayer);
1661     g_free(desktop->_reconstruction_old_layer_id);
1662     desktop->_reconstruction_old_layer_id = NULL;
1663     // printf("Desktop, finishing reconstruction end\n");
1664     return;
1667 /**
1668  * Namedview_modified callback.
1669  */
1670 static void
1671 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1673     SPNamedView *nv=SP_NAMEDVIEW(obj);
1675     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1677         /* Show/hide page background */
1678         if (nv->pagecolor & 0xff) {
1679             sp_canvas_item_show (desktop->table);
1680             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1681             sp_canvas_item_move_to_z (desktop->table, 0);
1682         } else {
1683             sp_canvas_item_hide (desktop->table);
1684         }
1686         /* Show/hide page border */
1687         if (nv->showborder) {
1688             // show
1689             sp_canvas_item_show (desktop->page_border);
1690             // set color and shadow
1691             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1692             if (nv->pageshadow) {
1693                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1694             }
1695             // place in the z-order stack
1696             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1697                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1698             } else {
1699                 int order = sp_canvas_item_order (desktop->page_border);
1700                 int morder = sp_canvas_item_order (desktop->drawing);
1701                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1702                     morder - order);
1703             }
1704         } else {
1705                 sp_canvas_item_hide (desktop->page_border);
1706                 if (nv->pageshadow) {
1707                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1708                 }
1709         }
1711         /* Show/hide page shadow */
1712         if (nv->showpageshadow && nv->pageshadow) {
1713             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1714         } else {
1715             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1716         }
1718         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1719         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1720             (SP_RGBA32_R_U(nv->pagecolor) +
1721              SP_RGBA32_G_U(nv->pagecolor) +
1722              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1723             // the background color is light or transparent, use black outline
1724             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1725         } else { // use white outline
1726             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1727         }
1728     }
1731 Geom::Matrix SPDesktop::w2d() const
1733     return _w2d;
1736 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1738     return p * _w2d;
1741 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1743     return p * _d2w;
1746 Geom::Matrix SPDesktop::doc2dt() const
1748     return _doc2dt;
1751 Geom::Matrix SPDesktop::dt2doc() const
1753     // doc2dt is its own inverse
1754     return _doc2dt;
1757 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1759     return p * _doc2dt;
1762 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1764     return p * dt2doc();
1768 /*
1769  * Pop event context from desktop's context stack. Never used.
1770  */
1771 // void
1772 // SPDesktop::pop_event_context (unsigned int key)
1773 // {
1774 //    SPEventContext *ec = NULL;
1775 //
1776 //    if (event_context && event_context->key == key) {
1777 //        g_return_if_fail (event_context);
1778 //        g_return_if_fail (event_context->next);
1779 //        ec = event_context;
1780 //        sp_event_context_deactivate (ec);
1781 //        event_context = ec->next;
1782 //        sp_event_context_activate (event_context);
1783 //        _event_context_changed_signal.emit (this, ec);
1784 //    }
1785 //
1786 //    SPEventContext *ref = event_context;
1787 //    while (ref && ref->next && ref->next->key != key)
1788 //        ref = ref->next;
1789 //
1790 //    if (ref && ref->next) {
1791 //        ec = ref->next;
1792 //        ref->next = ec->next;
1793 //    }
1794 //
1795 //    if (ec) {
1796 //        sp_event_context_finish (ec);
1797 //        g_object_unref (G_OBJECT (ec));
1798 //    }
1799 // }
1801 /*
1802   Local Variables:
1803   mode:c++
1804   c-file-style:"stroustrup"
1805   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1806   indent-tabs-mode:nil
1807   fill-column:99
1808   End:
1809 */
1810 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :