Code

Pot and Dutch translation update
[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::Scale(1, -1) ),
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     _doc2dt[5] = sp_document_height (document);
276     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
278     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
280     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
281             SP_CANVAS_ARENA (drawing)->arena,
282             dkey,
283             SP_ITEM_SHOW_DISPLAY);
284     if (ai) {
285         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
286     }
288     namedview->show(this);
289     /* Ugly hack */
290     activate_guides (true);
291     /* Ugly hack */
292     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
294 /* Set up notification of rebuilding the document, this allows
295        for saving object related settings in the document. */
296     _reconstruction_start_connection =
297         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
298     _reconstruction_finish_connection =
299         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
300     _reconstruction_old_layer_id = NULL;
302     // ?
303     // sp_active_desktop_set (desktop);
304     _inkscape = INKSCAPE;
306     _activate_connection = _activate_signal.connect(
307         sigc::bind(
308             sigc::ptr_fun(_onActivate),
309             this
310         )
311     );
312      _deactivate_connection = _deactivate_signal.connect(
313         sigc::bind(
314             sigc::ptr_fun(_onDeactivate),
315             this
316         )
317     );
319     _sel_modified_connection = selection->connectModified(
320         sigc::bind(
321             sigc::ptr_fun(&_onSelectionModified),
322             this
323         )
324     );
325     _sel_changed_connection = selection->connectChanged(
326         sigc::bind(
327             sigc::ptr_fun(&_onSelectionChanged),
328             this
329         )
330     );
333     /* setup LayerManager */
334     //   (Setting up after the connections are all in place, as it may use some of them)
335     layer_manager = new Inkscape::LayerManager( this );
337     showGrids(namedview->grids_visible, false);
339     temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
340     snapindicator = new Inkscape::Display::SnapIndicator ( this );
344 void SPDesktop::destroy()
346     if (snapindicator) {
347         delete snapindicator;
348         snapindicator = NULL;
349     }
350     if (temporary_item_list) {
351         delete temporary_item_list;
352         temporary_item_list = NULL;
353     }
355     if (selection) {
356         delete selection;
357         selection = NULL;
358     }
360     namedview->hide(this);
362     _activate_connection.disconnect();
363     _deactivate_connection.disconnect();
364     _sel_modified_connection.disconnect();
365     _sel_changed_connection.disconnect();
366     _modified_connection.disconnect();
367     _commit_connection.disconnect();
368     _reconstruction_start_connection.disconnect();
369     _reconstruction_finish_connection.disconnect();
371     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
372     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
373     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
375     while (event_context) {
376         SPEventContext *ec = event_context;
377         event_context = ec->next;
378         sp_event_context_finish (ec);
379         g_object_unref (G_OBJECT (ec));
380     }
382     if (_layer_hierarchy) {
383         delete _layer_hierarchy;
384 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
385     }
387     if (layer_manager) {
388         delete layer_manager;
389         layer_manager = NULL;
390     }
392     if (_inkscape) {
393         _inkscape = NULL;
394     }
396     if (drawing) {
397         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
398         drawing = NULL;
399     }
401     delete _guides_message_context;
402     _guides_message_context = NULL;
404     g_list_free (zooms_past);
405     g_list_free (zooms_future);
408 SPDesktop::~SPDesktop() {}
410 //--------------------------------------------------------------------
411 /* Public methods */
414 /* These methods help for temporarily showing things on-canvas.
415  * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
416  * is when you want to prematurely remove the item from the canvas, by calling
417  * desktop->remove_temporary_canvasitem(tempitem).
418  */
419 /** Note that lifetime is measured in milliseconds
420  * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
421  * delete the object for you and the reference will become invalid without you knowing it.
422  * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
423  * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
424  * because the object might be deleted already without you knowing it.
425  * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
426  */
427 Inkscape::Display::TemporaryItem *
428 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
430     if (move_to_bottom) {
431         sp_canvas_item_move_to_z(item, 0);
432     }
434     return temporary_item_list->add_item(item, lifetime);
437 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
438 */
439 void
440 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
442     // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
443     if (tempitem && temporary_item_list) {
444         temporary_item_list->delete_item(tempitem);
445     }
448 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
449     SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
450     canvas->rendermode = mode;
451     _display_mode = mode;
452     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
453     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
456 void SPDesktop::displayModeToggle() {
457     switch (_display_mode) {
458     case Inkscape::RENDERMODE_NORMAL:
459         _setDisplayMode(Inkscape::RENDERMODE_NO_FILTERS);
460         break;
461     case Inkscape::RENDERMODE_NO_FILTERS:
462         _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
463         break;
464     case Inkscape::RENDERMODE_OUTLINE:
465         _setDisplayMode(Inkscape::RENDERMODE_NORMAL);
466         break;
467 //    case Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW:
468     default:
469         _setDisplayMode(Inkscape::RENDERMODE_NORMAL);
470     }
473 /**
474  * Returns current root (=bottom) layer.
475  */
476 SPObject *SPDesktop::currentRoot() const
478     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
481 /**
482  * Returns current top layer.
483  */
484 SPObject *SPDesktop::currentLayer() const
486     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
489 /**
490  * Sets the current layer of the desktop.
491  *
492  * Make \a object the top layer.
493  */
494 void SPDesktop::setCurrentLayer(SPObject *object) {
495     g_return_if_fail(SP_IS_GROUP(object));
496     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
497     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
498     _layer_hierarchy->setBottom(object);
501 void SPDesktop::toggleLayerSolo(SPObject *object) {
502     g_return_if_fail(SP_IS_GROUP(object));
503     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
505     bool othersShowing = false;
506     std::vector<SPObject*> layers;
507     for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
508         layers.push_back(obj);
509         othersShowing |= !SP_ITEM(obj)->isHidden();
510     }
511     for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
512         layers.push_back(obj);
513         othersShowing |= !SP_ITEM(obj)->isHidden();
514     }
517     if ( SP_ITEM(object)->isHidden() ) {
518         SP_ITEM(object)->setHidden(false);
519     }
521     for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
522         SP_ITEM(*it)->setHidden(othersShowing);
523     }
526 /**
527  * Return layer that contains \a object.
528  */
529 SPObject *SPDesktop::layerForObject(SPObject *object) {
530     g_return_val_if_fail(object != NULL, NULL);
532     SPObject *root=currentRoot();
533     object = SP_OBJECT_PARENT(object);
534     while ( object && object != root && !isLayer(object) ) {
535         object = SP_OBJECT_PARENT(object);
536     }
537     return object;
540 /**
541  * True if object is a layer.
542  */
543 bool SPDesktop::isLayer(SPObject *object) const {
544     return ( SP_IS_GROUP(object)
545              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
546                   == SPGroup::LAYER ) );
549 /**
550  * True if desktop viewport fully contains \a item's bbox.
551  */
552 bool SPDesktop::isWithinViewport (SPItem *item) const
554     Geom::Rect const viewport = get_display_area();
555     Geom::OptRect const bbox = sp_item_bbox_desktop(item);
556     if (bbox) {
557         return viewport.contains(*bbox);
558     } else {
559         return true;
560     }
563 ///
564 bool SPDesktop::itemIsHidden(SPItem const *item) const {
565     return item->isHidden(this->dkey);
568 /**
569  * Set activate property of desktop; emit signal if changed.
570  */
571 void
572 SPDesktop::set_active (bool new_active)
574     if (new_active != _active) {
575         _active = new_active;
576         if (new_active) {
577             _activate_signal.emit();
578         } else {
579             _deactivate_signal.emit();
580         }
581     }
584 /**
585  * Set activate status of current desktop's named view.
586  */
587 void
588 SPDesktop::activate_guides(bool activate)
590     guides_active = activate;
591     namedview->activateGuides(this, activate);
594 /**
595  * Make desktop switch documents.
596  */
597 void
598 SPDesktop::change_document (SPDocument *theDocument)
600     g_return_if_fail (theDocument != NULL);
602     /* unselect everything before switching documents */
603     selection->clear();
605     setDocument (theDocument);
607     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
608        (this can probably be done in a better way) */
609     Gtk::Window *parent = this->getToplevel();
610     g_assert(parent != NULL);
611     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
612     if (dtw) {
613         dtw->desktop = this;
614     }
615     dtw->updateNamedview();
617     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
618     _document_replaced_signal.emit (this, theDocument);
621 /**
622  * Make desktop switch event contexts.
623  */
624 void
625 SPDesktop::set_event_context (GtkType type, const gchar *config)
627     SPEventContext *ec;
628     while (event_context) {
629         ec = event_context;
630         sp_event_context_deactivate (ec);
631         // we have to keep event_context valid during destruction - otherwise writing
632         // destructors is next to impossible
633         SPEventContext *next = ec->next;
634         sp_event_context_finish (ec);
635         g_object_unref (G_OBJECT (ec));
636         event_context = next;
637     }
639     // The event_context will be null. This means that it will be impossible
640     // to process any event invoked by the lines below. See for example bug
641     // LP #622350. Cutting and undoing again in the node tool resets the event
642     // context to the node tool. In this bug the line bellow invokes GDK_LEAVE_NOTIFY
643     // events which cannot be handled and must be discarded.
644     ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
645     ec->next = event_context;
646     event_context = ec;
647     // Now the event_context has been set again and we can process all events again
648     sp_event_context_activate (ec);
649     _event_context_changed_signal.emit (this, ec);
652 /**
653  * Push event context onto desktop's context stack.
654  */
655 void
656 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
658     SPEventContext *ref, *ec;
660     if (event_context && event_context->key == key) return;
661     ref = event_context;
662     while (ref && ref->next && ref->next->key != key) ref = ref->next;
663     if (ref && ref->next) {
664         ec = ref->next;
665         ref->next = ec->next;
666         sp_event_context_finish (ec);
667         g_object_unref (G_OBJECT (ec));
668     }
670     if (event_context) sp_event_context_deactivate (event_context);
671     ec = sp_event_context_new (type, this, config, key);
672     ec->next = event_context;
673     event_context = ec;
674     sp_event_context_activate (ec);
675     _event_context_changed_signal.emit (this, ec);
678 /**
679  * Sets the coordinate status to a given point
680  */
681 void
682 SPDesktop::set_coordinate_status (Geom::Point p) {
683     _widget->setCoordinateStatus(p);
686 /**
687  * \see sp_document_item_from_list_at_point_bottom()
688  */
689 SPItem *
690 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
692     g_return_val_if_fail (doc() != NULL, NULL);
693     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
696 /**
697  * \see sp_document_item_at_point()
698  */
699 SPItem *
700 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
702     g_return_val_if_fail (doc() != NULL, NULL);
703     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
706 /**
707  * \see sp_document_group_at_point()
708  */
709 SPItem *
710 SPDesktop::group_at_point (Geom::Point const p) const
712     g_return_val_if_fail (doc() != NULL, NULL);
713     return sp_document_group_at_point (doc(), dkey, p);
716 /**
717  * \brief  Returns the mouse point in document coordinates; if mouse is
718  * outside the canvas, returns the center of canvas viewpoint
719  */
720 Geom::Point
721 SPDesktop::point() const
723     Geom::Point p = _widget->getPointer();
724     Geom::Point pw = sp_canvas_window_to_world (canvas, p);
725     p = w2d(pw);
727     Geom::Rect const r = canvas->getViewbox();
729     Geom::Point r0 = w2d(r.min());
730     Geom::Point r1 = w2d(r.max());
732     if (p[Geom::X] >= r0[Geom::X] &&
733         p[Geom::X] <= r1[Geom::X] &&
734         p[Geom::Y] >= r1[Geom::Y] &&
735         p[Geom::Y] <= r0[Geom::Y])
736     {
737         return p;
738     } else {
739         return (r0 + r1) / 2;
740     }
743 /**
744  * Put current zoom data in history list.
745  */
746 void
747 SPDesktop::push_current_zoom (GList **history)
749     Geom::Rect const area = get_display_area();
751     NRRect *old_zoom = g_new(NRRect, 1);
752     old_zoom->x0 = area.min()[Geom::X];
753     old_zoom->x1 = area.max()[Geom::X];
754     old_zoom->y0 = area.min()[Geom::Y];
755     old_zoom->y1 = area.max()[Geom::Y];
756     if ( *history == NULL
757          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
758                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
759                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
760                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
761     {
762         *history = g_list_prepend (*history, old_zoom);
763     }
766 /**
767  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
768  */
769 void
770 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
772     g_assert(_widget);
774     // save the zoom
775     if (log) {
776         push_current_zoom(&zooms_past);
777         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
778         g_list_free (zooms_future);
779         zooms_future = NULL;
780     }
782     double const cx = 0.5 * (x0 + x1);
783     double const cy = 0.5 * (y0 + y1);
785     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
786     Geom::Rect viewbox = canvas->getViewbox();
787     viewbox.expandBy(-border);
789     double scale = _d2w.descrim();
790     double newscale;
791     if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
792         newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
793     } else {
794         newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
795     }
797     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
799     int clear = FALSE;
800     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
801         // zoom changed - set new zoom factors
802         _d2w = Geom::Scale(newscale, -newscale);
803         _w2d = Geom::Scale(1/newscale, 1/-newscale);
804         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
805         clear = TRUE;
806         signal_zoom_changed.emit(_d2w.descrim());
807     }
809     /* Calculate top left corner (in document pixels) */
810     x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
811     y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
813     /* Scroll */
814     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
816     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
817     sp_box3d_context_update_lines(event_context);
819     _widget->updateRulers();
820     _widget->updateScrollbars(_d2w.descrim());
821     _widget->updateZoom();
824 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
826     set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
829 /**
830  * Return viewbox dimensions.
831  */
832 Geom::Rect SPDesktop::get_display_area() const
834     Geom::Rect const viewbox = canvas->getViewbox();
836     double const scale = _d2w[0];
838     return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
839                       Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
842 /**
843  * Revert back to previous zoom if possible.
844  */
845 void
846 SPDesktop::prev_zoom()
848     if (zooms_past == NULL) {
849         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
850         return;
851     }
853     // push current zoom into forward zooms list
854     push_current_zoom (&zooms_future);
856     // restore previous zoom
857     set_display_area (((NRRect *) zooms_past->data)->x0,
858             ((NRRect *) zooms_past->data)->y0,
859             ((NRRect *) zooms_past->data)->x1,
860             ((NRRect *) zooms_past->data)->y1,
861             0, false);
863     // remove the just-added zoom from the past zooms list
864     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
867 /**
868  * Set zoom to next in list.
869  */
870 void
871 SPDesktop::next_zoom()
873     if (zooms_future == NULL) {
874         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
875         return;
876     }
878     // push current zoom into past zooms list
879     push_current_zoom (&zooms_past);
881     // restore next zoom
882     set_display_area (((NRRect *) zooms_future->data)->x0,
883             ((NRRect *) zooms_future->data)->y0,
884             ((NRRect *) zooms_future->data)->x1,
885             ((NRRect *) zooms_future->data)->y1,
886             0, false);
888     // remove the just-used zoom from the zooms_future list
889     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
892 /** \brief  Performs a quick zoom into what the user is working on
893     \param  enable  Whether we're going in or out of quick zoom
895 */
896 void
897 SPDesktop::zoom_quick (bool enable)
899     if (enable == _quick_zoom_enabled) {
900         return;
901     }
903     if (enable == true) {
904         _quick_zoom_stored_area = get_display_area();
905         bool zoomed = false;
907         // TODO This needs to migrate into the node tool, but currently the design
908         // of this method is sufficiently wrong to prevent this.
909         if (!zoomed && INK_IS_NODE_TOOL(event_context)) {
910             InkNodeTool *nt = static_cast<InkNodeTool*>(event_context);
911             if (!nt->_selected_nodes->empty()) {
912                 Geom::Rect nodes = *nt->_selected_nodes->bounds();
913                 double area = nodes.area();
914                 // do not zoom if a single cusp node is selected aand the bounds
915                 // have zero area.
916                 if (!Geom::are_near(area, 0) && area * 2.0 < _quick_zoom_stored_area.area()) {
917                     set_display_area(nodes, true);
918                     zoomed = true;
919                 }
920             }
921         }
923         if (!zoomed) {
924             Geom::OptRect const d = selection->bounds();
925             if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
926                 set_display_area(*d, true);
927                 zoomed = true;
928             }
929         }
931         if (!zoomed) {
932             zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
933             zoomed = true;
934         }
935     } else {
936         set_display_area(_quick_zoom_stored_area, false);
937     }
939     _quick_zoom_enabled = enable;
940     return;
943 /**
944  * Zoom to point with absolute zoom factor.
945  */
946 void
947 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
949     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
951     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
952     // this check prevents "sliding" when trying to zoom in at maximum zoom;
953     /// \todo someone please fix calculations properly and remove this hack
954     if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
955         return;
957     Geom::Rect const viewbox = canvas->getViewbox();
959     double const width2 = viewbox.dimensions()[Geom::X] / zoom;
960     double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
962     set_display_area(cx - px * width2,
963                      cy - py * height2,
964                      cx + (1 - px) * width2,
965                      cy + (1 - py) * height2,
966                      0.0);
969 /**
970  * Zoom to center with absolute zoom factor.
971  */
972 void
973 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
975     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
978 /**
979  * Zoom to point with relative zoom factor.
980  */
981 void
982 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
984     Geom::Rect const area = get_display_area();
986     if (cx < area.min()[Geom::X]) {
987         cx = area.min()[Geom::X];
988     }
989     if (cx > area.max()[Geom::X]) {
990         cx = area.max()[Geom::X];
991     }
992     if (cy < area.min()[Geom::Y]) {
993         cy = area.min()[Geom::Y];
994     }
995     if (cy > area.max()[Geom::Y]) {
996         cy = area.max()[Geom::Y];
997     }
999     gdouble const scale = _d2w.descrim() * zoom;
1000     double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
1001     double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
1003     zoom_absolute_keep_point(cx, cy, px, py, scale);
1006 /**
1007  * Zoom to center with relative zoom factor.
1008  */
1009 void
1010 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1012     gdouble scale = _d2w.descrim() * zoom;
1013     zoom_absolute (cx, cy, scale);
1016 /**
1017  * Set display area to origin and current document dimensions.
1018  */
1019 void
1020 SPDesktop::zoom_page()
1022     Geom::Rect d(Geom::Point(0, 0),
1023                  Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1025     if (d.minExtent() < 1.0) {
1026         return;
1027     }
1029     set_display_area(d, 10);
1032 /**
1033  * Set display area to current document width.
1034  */
1035 void
1036 SPDesktop::zoom_page_width()
1038     Geom::Rect const a = get_display_area();
1040     if (sp_document_width(doc()) < 1.0) {
1041         return;
1042     }
1044     Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1045                  Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1047     set_display_area(d, 10);
1050 /**
1051  * Zoom to selection.
1052  */
1053 void
1054 SPDesktop::zoom_selection()
1056     Geom::OptRect const d = selection->bounds();
1058     if ( !d || d->minExtent() < 0.1 ) {
1059         return;
1060     }
1062     set_display_area(*d, 10);
1065 /**
1066  * Tell widget to let zoom widget grab keyboard focus.
1067  */
1068 void
1069 SPDesktop::zoom_grab_focus()
1071     _widget->letZoomGrabFocus();
1074 /**
1075  * Zoom to whole drawing.
1076  */
1077 void
1078 SPDesktop::zoom_drawing()
1080     g_return_if_fail (doc() != NULL);
1081     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1082     g_return_if_fail (docitem != NULL);
1084     Geom::OptRect d = sp_item_bbox_desktop(docitem);
1086     /* Note that the second condition here indicates that
1087     ** there are no items in the drawing.
1088     */
1089     if ( !d || d->minExtent() < 0.1 ) {
1090         return;
1091     }
1093     set_display_area(*d, 10);
1096 /**
1097  * Scroll canvas by specific coordinate amount in svg coordinates.
1098  */
1099 void
1100 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1102     double scale = _d2w.descrim();
1103     scroll_world(dx*scale, dy*scale, is_scrolling);
1106 /**
1107  * Scroll canvas by specific coordinate amount.
1108  */
1109 void
1110 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1112     g_assert(_widget);
1114     Geom::Rect const viewbox = canvas->getViewbox();
1116     sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1118     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1119     sp_box3d_context_update_lines(event_context);
1121     _widget->updateRulers();
1122     _widget->updateScrollbars(_d2w.descrim());
1125 bool
1126 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1128     using Geom::X;
1129     using Geom::Y;
1131     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1132     gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1134     // autoscrolldistance is in screen pixels, but the display area is in document units
1135     autoscrolldistance /= _d2w.descrim();
1136     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1137     Geom::Rect dbox = get_display_area();
1138     dbox.expandBy(-autoscrolldistance);
1140     if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1141         !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y])   ) {
1143         Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1145         gdouble x_to;
1146         if (p[X] < dbox.min()[X])
1147             x_to = dbox.min()[X];
1148         else if (p[X] > dbox.max()[X])
1149             x_to = dbox.max()[X];
1150         else
1151             x_to = p[X];
1153         gdouble y_to;
1154         if (p[Y] < dbox.min()[Y])
1155             y_to = dbox.min()[Y];
1156         else if (p[Y] > dbox.max()[Y])
1157             y_to = dbox.max()[Y];
1158         else
1159             y_to = p[Y];
1161         Geom::Point const d_dt(x_to, y_to);
1162         Geom::Point const d_w( d_dt * _d2w );
1163         Geom::Point const moved_w( d_w - s_w );
1165         if (autoscrollspeed == 0)
1166             autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1168         if (autoscrollspeed != 0)
1169             scroll_world (autoscrollspeed * moved_w);
1171         return true;
1172     }
1173     return false;
1176 bool
1177 SPDesktop::is_iconified()
1179     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1182 void
1183 SPDesktop::iconify()
1185     _widget->setIconified();
1188 bool
1189 SPDesktop::is_maximized()
1191     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1194 void
1195 SPDesktop::maximize()
1197     _widget->setMaximized();
1200 bool
1201 SPDesktop::is_fullscreen()
1203     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1206 void
1207 SPDesktop::fullscreen()
1209     _widget->setFullscreen();
1212 /** \brief  Checks to see if the user is working in focused mode
1214     Returns the value of \c _focusMode
1215 */
1216 bool
1217 SPDesktop::is_focusMode()
1219     return _focusMode;
1222 /** \brief  Changes whether the user is in focus mode or not
1223     \param  mode  Which mode the view should be in
1225 */
1226 void
1227 SPDesktop::focusMode (bool mode)
1229     if (mode == _focusMode) { return; }
1231     _focusMode = mode;
1233     layoutWidget();
1234     //sp_desktop_widget_layout(SPDesktopWidget);
1236     return;
1239 void
1240 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1242     _widget->getGeometry (x, y, w, h);
1245 void
1246 SPDesktop::setWindowPosition (Geom::Point p)
1248     _widget->setPosition (p);
1251 void
1252 SPDesktop::setWindowSize (gint w, gint h)
1254     _widget->setSize (w, h);
1257 void
1258 SPDesktop::setWindowTransient (void *p, int transient_policy)
1260     _widget->setTransient (p, transient_policy);
1263 Gtk::Window*
1264 SPDesktop::getToplevel( )
1266     return _widget->getWindow();
1269 void
1270 SPDesktop::presentWindow()
1272     _widget->present();
1275 bool
1276 SPDesktop::warnDialog (gchar *text)
1278     return _widget->warnDialog (text);
1281 void
1282 SPDesktop::toggleRulers()
1284     _widget->toggleRulers();
1287 void
1288 SPDesktop::toggleScrollbars()
1290     _widget->toggleScrollbars();
1293 void
1294 SPDesktop::layoutWidget()
1296     _widget->layout();
1299 void
1300 SPDesktop::destroyWidget()
1302     _widget->destroy();
1305 bool
1306 SPDesktop::shutdown()
1308     return _widget->shutdown();
1311 bool SPDesktop::onDeleteUI (GdkEventAny*)
1313     if(shutdown())
1314         return true;
1316     destroyWidget();
1317     return false;
1320 /**
1321  *  onWindowStateEvent
1322  *
1323  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1324  *  Since GTK doesn't have a way to query this state information directly, we
1325  *  record it for the desktop here, and also possibly trigger a layout.
1326  */
1327 bool
1328 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1330     // Record the desktop window's state
1331     window_state = event->new_window_state;
1333     // Layout may differ depending on full-screen mode or not
1334     GdkWindowState changed = event->changed_mask;
1335     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1336         layoutWidget();
1337     }
1339     return false;
1342 void
1343 SPDesktop::setToolboxFocusTo (gchar const *label)
1345     _widget->setToolboxFocusTo (label);
1348 void
1349 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1351     _widget->setToolboxAdjustmentValue (id, val);
1354 void
1355 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1357     _widget->setToolboxSelectOneValue (id, val);
1360 bool
1361 SPDesktop::isToolboxButtonActive (gchar const *id)
1363     return _widget->isToolboxButtonActive (id);
1366 void
1367 SPDesktop::emitToolSubselectionChanged(gpointer data)
1369     _tool_subselection_changed.emit(data);
1370     inkscape_subselection_changed (this);
1373 void
1374 SPDesktop::updateNow()
1376     sp_canvas_update_now(canvas);
1379 void
1380 SPDesktop::enableInteraction()
1382   _widget->enableInteraction();
1385 void SPDesktop::disableInteraction()
1387   _widget->disableInteraction();
1390 void SPDesktop::setWaitingCursor()
1392     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1393     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1394     gdk_cursor_unref(waiting);
1395     // GDK needs the flush for the cursor change to take effect
1396     gdk_flush();
1397     waiting_cursor = true;
1400 void SPDesktop::clearWaitingCursor()
1402   if (waiting_cursor)
1403       sp_event_context_update_cursor(sp_desktop_event_context(this));
1406 void SPDesktop::toggleColorProfAdjust()
1408     _widget->toggleColorProfAdjust();
1411 void SPDesktop::toggleGrids()
1413     if (namedview->grids) {
1414         if(gridgroup) {
1415             showGrids(!grids_visible);
1416         }
1417     } else {
1418         //there is no grid present at the moment. add a rectangular grid and make it visible
1419         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1420         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1421         showGrids(true);
1422     }
1425 void SPDesktop::showGrids(bool show, bool dirty_document)
1427     grids_visible = show;
1428     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1429     if (show) {
1430         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1431     } else {
1432         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1433     }
1436 void SPDesktop::toggleSnapGlobal()
1438     bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1439     Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1440     sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1443 //----------------------------------------------------------------------
1444 // Callback implementations. The virtual ones are connected by the view.
1446 void
1447 SPDesktop::onPositionSet (double x, double y)
1449     _widget->viewSetPosition (Geom::Point(x,y));
1452 void
1453 SPDesktop::onResized (double /*x*/, double /*y*/)
1455    // Nothing called here
1458 /**
1459  * Redraw callback; queues Gtk redraw; connected by View.
1460  */
1461 void
1462 SPDesktop::onRedrawRequested ()
1464     if (main) {
1465         _widget->requestCanvasUpdate();
1466     }
1469 void
1470 SPDesktop::updateCanvasNow()
1472   _widget->requestCanvasUpdateAndWait();
1475 /**
1476  * Associate document with desktop.
1477  */
1478 void
1479 SPDesktop::setDocument (SPDocument *doc)
1481     if (this->doc() && doc) {
1482         namedview->hide(this);
1483         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1484     }
1486     if (_layer_hierarchy) {
1487         _layer_hierarchy->clear();
1488         delete _layer_hierarchy;
1489     }
1490     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1491     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1492     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1493     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1494     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1496     /* setup EventLog */
1497     event_log = new Inkscape::EventLog(doc);
1498     doc->addUndoObserver(*event_log);
1500     _commit_connection.disconnect();
1501     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1503     /// \todo fixme: This condition exists to make sure the code
1504     /// inside is NOT called on initialization, only on replacement. But there
1505     /// are surely more safe methods to accomplish this.
1506     // TODO since the comment had reversed logic, check the intent of this block of code:
1507     if (drawing) {
1508         NRArenaItem *ai = 0;
1510         namedview = sp_document_namedview (doc, NULL);
1511         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1512         number = namedview->getViewCount();
1514         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1515                 SP_CANVAS_ARENA (drawing)->arena,
1516                 dkey,
1517                 SP_ITEM_SHOW_DISPLAY);
1518         if (ai) {
1519             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1520         }
1521         namedview->show(this);
1522         /* Ugly hack */
1523         activate_guides (true);
1524         /* Ugly hack */
1525         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1526     }
1528     _document_replaced_signal.emit (this, doc);
1530     View::setDocument (doc);
1533 void
1534 SPDesktop::onStatusMessage
1535 (Inkscape::MessageType type, gchar const *message)
1537     if (_widget) {
1538         _widget->setMessage(type, message);
1539     }
1542 void
1543 SPDesktop::onDocumentURISet (gchar const* uri)
1545     _widget->setTitle(uri);
1548 /**
1549  * Resized callback.
1550  */
1551 void
1552 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1554     _doc2dt[5] = height;
1555     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1556     Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1557     SP_CTRLRECT(page)->setRectangle(a);
1558     SP_CTRLRECT(page_border)->setRectangle(a);
1562 void
1563 SPDesktop::_onActivate (SPDesktop* dt)
1565     if (!dt->_widget) return;
1566     dt->_widget->activateDesktop();
1569 void
1570 SPDesktop::_onDeactivate (SPDesktop* dt)
1572     if (!dt->_widget) return;
1573     dt->_widget->deactivateDesktop();
1576 void
1577 SPDesktop::_onSelectionModified
1578 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1580     if (!dt->_widget) return;
1581     dt->_widget->updateScrollbars (dt->_d2w.descrim());
1584 static void
1585 _onSelectionChanged
1586 (Inkscape::Selection *selection, SPDesktop *desktop)
1588     /** \todo
1589      * only change the layer for single selections, or what?
1590      * This seems reasonable -- for multiple selections there can be many
1591      * different layers involved.
1592      */
1593     SPItem *item=selection->singleItem();
1594     if (item) {
1595         SPObject *layer=desktop->layerForObject(item);
1596         if ( layer && layer != desktop->currentLayer() ) {
1597             desktop->setCurrentLayer(layer);
1598         }
1599     }
1602 /**
1603  * Calls event handler of current event context.
1604  * \param arena Unused
1605  * \todo fixme
1606  */
1607 static gint
1608 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1610     if (ai) {
1611         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1612         return sp_event_context_item_handler (desktop->event_context, spi, event);
1613     } else {
1614         return sp_event_context_root_handler (desktop->event_context, event);
1615     }
1618 static void
1619 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1620     g_return_if_fail(SP_IS_GROUP(layer));
1621     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1624 /// Callback
1625 static void
1626 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1627     g_return_if_fail(SP_IS_GROUP(layer));
1628     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1631 /// Callback
1632 static void
1633 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1634                                          SPDesktop *desktop)
1636     desktop->_layer_changed_signal.emit (bottom);
1639 /// Called when document is starting to be rebuilt.
1640 static void
1641 _reconstruction_start (SPDesktop * desktop)
1643     // printf("Desktop, starting reconstruction\n");
1644     desktop->_reconstruction_old_layer_id = g_strdup(desktop->currentLayer()->getId());
1645     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1647     /*
1648     GSList const * selection_objs = desktop->selection->list();
1649     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1651     }
1652     */
1653     desktop->selection->clear();
1655     // printf("Desktop, starting reconstruction end\n");
1658 /// Called when document rebuild is finished.
1659 static void
1660 _reconstruction_finish (SPDesktop * desktop)
1662     // printf("Desktop, finishing reconstruction\n");
1663     if (desktop->_reconstruction_old_layer_id == NULL)
1664         return;
1666     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1667     if (newLayer != NULL)
1668         desktop->setCurrentLayer(newLayer);
1670     g_free(desktop->_reconstruction_old_layer_id);
1671     desktop->_reconstruction_old_layer_id = NULL;
1672     // printf("Desktop, finishing reconstruction end\n");
1673     return;
1676 /**
1677  * Namedview_modified callback.
1678  */
1679 static void
1680 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1682     SPNamedView *nv=SP_NAMEDVIEW(obj);
1684     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1686         /* Show/hide page background */
1687         if (nv->pagecolor & 0xff) {
1688             sp_canvas_item_show (desktop->table);
1689             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1690             sp_canvas_item_move_to_z (desktop->table, 0);
1691         } else {
1692             sp_canvas_item_hide (desktop->table);
1693         }
1695         /* Show/hide page border */
1696         if (nv->showborder) {
1697             // show
1698             sp_canvas_item_show (desktop->page_border);
1699             // set color and shadow
1700             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1701             if (nv->pageshadow) {
1702                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1703             }
1704             // place in the z-order stack
1705             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1706                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1707             } else {
1708                 int order = sp_canvas_item_order (desktop->page_border);
1709                 int morder = sp_canvas_item_order (desktop->drawing);
1710                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1711                     morder - order);
1712             }
1713         } else {
1714                 sp_canvas_item_hide (desktop->page_border);
1715                 if (nv->pageshadow) {
1716                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1717                 }
1718         }
1720         /* Show/hide page shadow */
1721         if (nv->showpageshadow && nv->pageshadow) {
1722             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1723         } else {
1724             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1725         }
1727         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1728         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1729             (SP_RGBA32_R_U(nv->pagecolor) +
1730              SP_RGBA32_G_U(nv->pagecolor) +
1731              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1732             // the background color is light or transparent, use black outline
1733             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1734         } else { // use white outline
1735             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1736         }
1737     }
1740 Geom::Matrix SPDesktop::w2d() const
1742     return _w2d;
1745 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1747     return p * _w2d;
1750 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1752     return p * _d2w;
1755 Geom::Matrix SPDesktop::doc2dt() const
1757     return _doc2dt;
1760 Geom::Matrix SPDesktop::dt2doc() const
1762     // doc2dt is its own inverse
1763     return _doc2dt;
1766 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1768     return p * _doc2dt;
1771 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1773     return p * dt2doc();
1777 /*
1778  * Pop event context from desktop's context stack. Never used.
1779  */
1780 // void
1781 // SPDesktop::pop_event_context (unsigned int key)
1782 // {
1783 //    SPEventContext *ec = NULL;
1784 //
1785 //    if (event_context && event_context->key == key) {
1786 //        g_return_if_fail (event_context);
1787 //        g_return_if_fail (event_context->next);
1788 //        ec = event_context;
1789 //        sp_event_context_deactivate (ec);
1790 //        event_context = ec->next;
1791 //        sp_event_context_activate (event_context);
1792 //        _event_context_changed_signal.emit (this, ec);
1793 //    }
1794 //
1795 //    SPEventContext *ref = event_context;
1796 //    while (ref && ref->next && ref->next->key != key)
1797 //        ref = ref->next;
1798 //
1799 //    if (ref && ref->next) {
1800 //        ec = ref->next;
1801 //        ref->next = ec->next;
1802 //    }
1803 //
1804 //    if (ec) {
1805 //        sp_event_context_finish (ec);
1806 //        g_object_unref (G_OBJECT (ec));
1807 //    }
1808 // }
1810 /*
1811   Local Variables:
1812   mode:c++
1813   c-file-style:"stroustrup"
1814   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1815   indent-tabs-mode:nil
1816   fill-column:99
1817   End:
1818 */
1819 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :