Code

GSoC node tool
[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 the braindead quick zoom implementation.
95 // Remove them after fixing this.
96 #include "ui/tool/node-tool.h"
97 #include "ui/tool/control-point-selection.h"
99 #include "display/sp-canvas.h"
101 namespace Inkscape { namespace XML { class Node; }}
103 // Callback declarations
104 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
105 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
106 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
107 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
108 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
109 static void _reconstruction_start(SPDesktop * desktop);
110 static void _reconstruction_finish(SPDesktop * desktop);
111 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
113 /**
114  * Return new desktop object.
115  * \pre namedview != NULL.
116  * \pre canvas != NULL.
117  */
118 SPDesktop::SPDesktop() :
119     _dlg_mgr( 0 ),
120     namedview( 0 ),
121     canvas( 0 ),
122     selection( 0 ),
123     event_context( 0 ),
124     layer_manager( 0 ),
125     event_log( 0 ),
126     temporary_item_list( 0 ),
127     snapindicator( 0 ),
128     acetate( 0 ),
129     main( 0 ),
130     gridgroup( 0 ),
131     guides( 0 ),
132     drawing( 0 ),
133     sketch( 0 ),
134     controls( 0 ),
135     tempgroup ( 0 ),
136     table( 0 ),
137     page( 0 ),
138     page_border( 0 ),
139     current( 0 ),
140     _focusMode(false),
141     zooms_past( 0 ),
142     zooms_future( 0 ),
143     dkey( 0 ),
144     number( 0 ),
145     window_state(0),
146     interaction_disabled_counter( 0 ),
147     waiting_cursor( false ),
148     guides_active( false ),
149     gr_item( 0 ),
150     gr_point_type( 0 ),
151     gr_point_i( 0 ),
152     gr_fill_or_stroke( true ),
153     _layer_hierarchy( 0 ),
154     _reconstruction_old_layer_id( 0 ),
155     _display_mode(Inkscape::RENDERMODE_NORMAL),
156     _widget( 0 ),
157     _inkscape( 0 ),
158     _guides_message_context( 0 ),
159     _active( false ),
160     _w2d(),
161     _d2w(),
162     _doc2dt( Geom::Scale(1, -1) ),
163     grids_visible( false )
165     _d2w.setIdentity();
166     _w2d.setIdentity();
168     selection = Inkscape::GC::release( new Inkscape::Selection(this) );
171 void
172 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas, Inkscape::UI::View::EditWidgetInterface *widget)
174     _widget = widget;
176     // Temporary workaround for link order issues:
177     Inkscape::DeviceManager::getManager().getDevices();
178     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
180     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
182     current = prefs->getStyle("/desktop/style");
184     namedview = nv;
185     canvas = aCanvas;
187     SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
188     /* Kill flicker */
189     sp_document_ensure_up_to_date (document);
191     /* Setup Dialog Manager */
192     _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
194     dkey = sp_item_display_key_new (1);
196     /* Connect document */
197     setDocument (document);
199     number = namedview->getViewCount();
202     /* Setup Canvas */
203     g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
205     SPCanvasGroup *root = sp_canvas_root (canvas);
207     /* Setup adminstrative layers */
208     acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
209     g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
210     main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
211     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
213     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
214     SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
215     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
216     sp_canvas_item_move_to_z (table, 0);
218     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
219     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
220     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
222     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
223     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
225     SP_CANVAS_ARENA (drawing)->arena->delta = prefs->getDouble("/options/cursortolerance/value", 1.0); // default is 1 px
227     if (prefs->getBool("/options/startmode/outline")) {
228         // Start in outline mode
229         setDisplayModeOutline();
230     } else {
231         // Start in normal mode, default
232         setDisplayModeNormal();
233     }
235     // The order in which these canvas items are added determines the z-order. It's therefore
236     // important to add the tempgroup (which will contain the snapindicator) before adding the
237     // controls. Only this way one will be able to quickly (before the snap indicator has
238     // disappeared) reselect a node after snapping it. If the z-order is wrong however, this
239     // will not work (the snap indicator is on top of the node handler; is the snapindicator
240     // being selected? or does it intercept some of the events that should have gone to the
241     // node handler? see bug https://bugs.launchpad.net/inkscape/+bug/414142)
242     gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
243     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
244     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
245     tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
246     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
248     /* Push select tool to the bottom of stack */
249     /** \todo
250      * FIXME: this is the only call to this.  Everything else seems to just
251      * call "set" instead of "push".  Can we assume that there is only one
252      * context ever?
253      */
254     push_event_context (SP_TYPE_SELECT_CONTEXT, "/tools/select", SP_EVENT_CONTEXT_STATIC);
256     // display rect and zoom are now handled in sp_desktop_widget_realize()
258     Geom::Rect const d(Geom::Point(0.0, 0.0),
259                        Geom::Point(sp_document_width(document), sp_document_height(document)));
261     SP_CTRLRECT(page)->setRectangle(d);
262     SP_CTRLRECT(page_border)->setRectangle(d);
264     /* the following sets the page shadow on the canvas
265        It was originally set to 5, which is really cheesy!
266        It now is an attribute in the document's namedview. If a value of
267        0 is used, then the constructor for a shadow is not initialized.
268     */
270     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
271         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
272     }
275     /* Connect event for page resize */
276     _doc2dt[5] = sp_document_height (document);
277     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
279     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
281     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
282             SP_CANVAS_ARENA (drawing)->arena,
283             dkey,
284             SP_ITEM_SHOW_DISPLAY);
285     if (ai) {
286         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
287     }
289     namedview->show(this);
290     /* Ugly hack */
291     activate_guides (true);
292     /* Ugly hack */
293     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
295 /* Set up notification of rebuilding the document, this allows
296        for saving object related settings in the document. */
297     _reconstruction_start_connection =
298         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
299     _reconstruction_finish_connection =
300         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
301     _reconstruction_old_layer_id = NULL;
303     // ?
304     // sp_active_desktop_set (desktop);
305     _inkscape = INKSCAPE;
307     _activate_connection = _activate_signal.connect(
308         sigc::bind(
309             sigc::ptr_fun(_onActivate),
310             this
311         )
312     );
313      _deactivate_connection = _deactivate_signal.connect(
314         sigc::bind(
315             sigc::ptr_fun(_onDeactivate),
316             this
317         )
318     );
320     _sel_modified_connection = selection->connectModified(
321         sigc::bind(
322             sigc::ptr_fun(&_onSelectionModified),
323             this
324         )
325     );
326     _sel_changed_connection = selection->connectChanged(
327         sigc::bind(
328             sigc::ptr_fun(&_onSelectionChanged),
329             this
330         )
331     );
334     /* setup LayerManager */
335     //   (Setting up after the connections are all in place, as it may use some of them)
336     layer_manager = new Inkscape::LayerManager( this );
338     showGrids(namedview->grids_visible, false);
340     temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
341     snapindicator = new Inkscape::Display::SnapIndicator ( this );
345 void SPDesktop::destroy()
347     if (snapindicator) {
348         delete snapindicator;
349         snapindicator = NULL;
350     }
351     if (temporary_item_list) {
352         delete temporary_item_list;
353         temporary_item_list = NULL;
354     }
356     if (selection) {
357         delete selection;
358         selection = NULL;
359     }
361     namedview->hide(this);
363     _activate_connection.disconnect();
364     _deactivate_connection.disconnect();
365     _sel_modified_connection.disconnect();
366     _sel_changed_connection.disconnect();
367     _modified_connection.disconnect();
368     _commit_connection.disconnect();
369     _reconstruction_start_connection.disconnect();
370     _reconstruction_finish_connection.disconnect();
372     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
373     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
374     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
376     while (event_context) {
377         SPEventContext *ec = event_context;
378         event_context = ec->next;
379         sp_event_context_finish (ec);
380         g_object_unref (G_OBJECT (ec));
381     }
383     if (_layer_hierarchy) {
384         delete _layer_hierarchy;
385 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
386     }
388     if (layer_manager) {
389         delete layer_manager;
390         layer_manager = NULL;
391     }
393     if (_inkscape) {
394         _inkscape = NULL;
395     }
397     if (drawing) {
398         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
399         drawing = NULL;
400     }
402     delete _guides_message_context;
403     _guides_message_context = NULL;
405     g_list_free (zooms_past);
406     g_list_free (zooms_future);
409 SPDesktop::~SPDesktop() {}
411 //--------------------------------------------------------------------
412 /* Public methods */
415 /* These methods help for temporarily showing things on-canvas.
416  * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
417  * is when you want to prematurely remove the item from the canvas, by calling
418  * desktop->remove_temporary_canvasitem(tempitem).
419  */
420 /** Note that lifetime is measured in milliseconds
421  * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
422  * delete the object for you and the reference will become invalid without you knowing it.
423  * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
424  * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
425  * because the object might be deleted already without you knowing it.
426  * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
427  */
428 Inkscape::Display::TemporaryItem *
429 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
431     if (move_to_bottom) {
432         sp_canvas_item_move_to_z(item, 0);
433     }
435     return temporary_item_list->add_item(item, lifetime);
438 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
439 */
440 void
441 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
443     // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
444     if (tempitem && temporary_item_list) {
445         temporary_item_list->delete_item(tempitem);
446     }
449 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
450     SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
451     canvas->rendermode = mode;
452     _display_mode = mode;
453     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
454     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
457 void SPDesktop::displayModeToggle() {
458     switch (_display_mode) {
459     case Inkscape::RENDERMODE_NORMAL:
460         _setDisplayMode(Inkscape::RENDERMODE_NO_FILTERS);
461         break;
462     case Inkscape::RENDERMODE_NO_FILTERS:
463         _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
464         break;
465     case Inkscape::RENDERMODE_OUTLINE:
466     default:
467         _setDisplayMode(Inkscape::RENDERMODE_NORMAL);
468     }
471 /**
472  * Returns current root (=bottom) layer.
473  */
474 SPObject *SPDesktop::currentRoot() const
476     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
479 /**
480  * Returns current top layer.
481  */
482 SPObject *SPDesktop::currentLayer() const
484     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
487 /**
488  * Sets the current layer of the desktop.
489  *
490  * Make \a object the top layer.
491  */
492 void SPDesktop::setCurrentLayer(SPObject *object) {
493     g_return_if_fail(SP_IS_GROUP(object));
494     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
495     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
496     _layer_hierarchy->setBottom(object);
499 void SPDesktop::toggleLayerSolo(SPObject *object) {
500     g_return_if_fail(SP_IS_GROUP(object));
501     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
503     bool othersShowing = false;
504     std::vector<SPObject*> layers;
505     for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
506         layers.push_back(obj);
507         othersShowing |= !SP_ITEM(obj)->isHidden();
508     }
509     for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
510         layers.push_back(obj);
511         othersShowing |= !SP_ITEM(obj)->isHidden();
512     }
515     if ( SP_ITEM(object)->isHidden() ) {
516         SP_ITEM(object)->setHidden(false);
517     }
519     for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
520         SP_ITEM(*it)->setHidden(othersShowing);
521     }
524 /**
525  * Return layer that contains \a object.
526  */
527 SPObject *SPDesktop::layerForObject(SPObject *object) {
528     g_return_val_if_fail(object != NULL, NULL);
530     SPObject *root=currentRoot();
531     object = SP_OBJECT_PARENT(object);
532     while ( object && object != root && !isLayer(object) ) {
533         object = SP_OBJECT_PARENT(object);
534     }
535     return object;
538 /**
539  * True if object is a layer.
540  */
541 bool SPDesktop::isLayer(SPObject *object) const {
542     return ( SP_IS_GROUP(object)
543              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
544                   == SPGroup::LAYER ) );
547 /**
548  * True if desktop viewport fully contains \a item's bbox.
549  */
550 bool SPDesktop::isWithinViewport (SPItem *item) const
552     Geom::Rect const viewport = get_display_area();
553     Geom::OptRect const bbox = sp_item_bbox_desktop(item);
554     if (bbox) {
555         return viewport.contains(*bbox);
556     } else {
557         return true;
558     }
561 ///
562 bool SPDesktop::itemIsHidden(SPItem const *item) const {
563     return item->isHidden(this->dkey);
566 /**
567  * Set activate property of desktop; emit signal if changed.
568  */
569 void
570 SPDesktop::set_active (bool new_active)
572     if (new_active != _active) {
573         _active = new_active;
574         if (new_active) {
575             _activate_signal.emit();
576         } else {
577             _deactivate_signal.emit();
578         }
579     }
582 /**
583  * Set activate status of current desktop's named view.
584  */
585 void
586 SPDesktop::activate_guides(bool activate)
588     guides_active = activate;
589     namedview->activateGuides(this, activate);
592 /**
593  * Make desktop switch documents.
594  */
595 void
596 SPDesktop::change_document (SPDocument *theDocument)
598     g_return_if_fail (theDocument != NULL);
600     /* unselect everything before switching documents */
601     selection->clear();
603     setDocument (theDocument);
605     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
606        (this can probably be done in a better way) */
607     Gtk::Window *parent = this->getToplevel();
608     g_assert(parent != NULL);
609     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
610     if (dtw) dtw->desktop = this;
611     sp_desktop_widget_update_namedview(dtw);
613     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
614     _document_replaced_signal.emit (this, theDocument);
617 /**
618  * Make desktop switch event contexts.
619  */
620 void
621 SPDesktop::set_event_context (GtkType type, const gchar *config)
623     SPEventContext *ec;
624     while (event_context) {
625         ec = event_context;
626         sp_event_context_deactivate (ec);
627         // we have to keep event_context valid during destruction - otherwise writing
628         // destructors is next to impossible
629         SPEventContext *next = ec->next;
630         sp_event_context_finish (ec);
631         g_object_unref (G_OBJECT (ec));
632         event_context = next;
633     }
635     ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
636     ec->next = event_context;
637     event_context = ec;
638     sp_event_context_activate (ec);
639     _event_context_changed_signal.emit (this, ec);
642 /**
643  * Push event context onto desktop's context stack.
644  */
645 void
646 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
648     SPEventContext *ref, *ec;
650     if (event_context && event_context->key == key) return;
651     ref = event_context;
652     while (ref && ref->next && ref->next->key != key) ref = ref->next;
653     if (ref && ref->next) {
654         ec = ref->next;
655         ref->next = ec->next;
656         sp_event_context_finish (ec);
657         g_object_unref (G_OBJECT (ec));
658     }
660     if (event_context) sp_event_context_deactivate (event_context);
661     ec = sp_event_context_new (type, this, config, key);
662     ec->next = event_context;
663     event_context = ec;
664     sp_event_context_activate (ec);
665     _event_context_changed_signal.emit (this, ec);
668 /**
669  * Sets the coordinate status to a given point
670  */
671 void
672 SPDesktop::set_coordinate_status (Geom::Point p) {
673     _widget->setCoordinateStatus(p);
676 /**
677  * \see sp_document_item_from_list_at_point_bottom()
678  */
679 SPItem *
680 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
682     g_return_val_if_fail (doc() != NULL, NULL);
683     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
686 /**
687  * \see sp_document_item_at_point()
688  */
689 SPItem *
690 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
692     g_return_val_if_fail (doc() != NULL, NULL);
693     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
696 /**
697  * \see sp_document_group_at_point()
698  */
699 SPItem *
700 SPDesktop::group_at_point (Geom::Point const p) const
702     g_return_val_if_fail (doc() != NULL, NULL);
703     return sp_document_group_at_point (doc(), dkey, p);
706 /**
707  * \brief  Returns the mouse point in document coordinates; if mouse is
708  * outside the canvas, returns the center of canvas viewpoint
709  */
710 Geom::Point
711 SPDesktop::point() const
713     Geom::Point p = _widget->getPointer();
714     Geom::Point pw = sp_canvas_window_to_world (canvas, p);
715     p = w2d(pw);
717     Geom::Rect const r = canvas->getViewbox();
719     Geom::Point r0 = w2d(r.min());
720     Geom::Point r1 = w2d(r.max());
722     if (p[Geom::X] >= r0[Geom::X] &&
723         p[Geom::X] <= r1[Geom::X] &&
724         p[Geom::Y] >= r1[Geom::Y] &&
725         p[Geom::Y] <= r0[Geom::Y])
726     {
727         return p;
728     } else {
729         return (r0 + r1) / 2;
730     }
733 /**
734  * Put current zoom data in history list.
735  */
736 void
737 SPDesktop::push_current_zoom (GList **history)
739     Geom::Rect const area = get_display_area();
741     NRRect *old_zoom = g_new(NRRect, 1);
742     old_zoom->x0 = area.min()[Geom::X];
743     old_zoom->x1 = area.max()[Geom::X];
744     old_zoom->y0 = area.min()[Geom::Y];
745     old_zoom->y1 = area.max()[Geom::Y];
746     if ( *history == NULL
747          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
748                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
749                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
750                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
751     {
752         *history = g_list_prepend (*history, old_zoom);
753     }
756 /**
757  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
758  */
759 void
760 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
762     g_assert(_widget);
764     // save the zoom
765     if (log) {
766         push_current_zoom(&zooms_past);
767         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
768         g_list_free (zooms_future);
769         zooms_future = NULL;
770     }
772     double const cx = 0.5 * (x0 + x1);
773     double const cy = 0.5 * (y0 + y1);
775     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
776     Geom::Rect viewbox = canvas->getViewbox();
777     viewbox.expandBy(-border);
779     double scale = _d2w.descrim();
780     double newscale;
781     if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
782         newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
783     } else {
784         newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
785     }
787     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
789     int clear = FALSE;
790     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
791         // zoom changed - set new zoom factors
792         _d2w = Geom::Scale(newscale, -newscale);
793         _w2d = Geom::Scale(1/newscale, 1/-newscale);
794         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
795         clear = TRUE;
796         signal_zoom_changed.emit(_d2w.descrim());
797     }
799     /* Calculate top left corner (in document pixels) */
800     x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
801     y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
803     /* Scroll */
804     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
806     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
807     sp_box3d_context_update_lines(event_context);
809     _widget->updateRulers();
810     _widget->updateScrollbars(_d2w.descrim());
811     _widget->updateZoom();
814 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
816     set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
819 /**
820  * Return viewbox dimensions.
821  */
822 Geom::Rect SPDesktop::get_display_area() const
824     Geom::Rect const viewbox = canvas->getViewbox();
826     double const scale = _d2w[0];
828     return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
829                       Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
832 /**
833  * Revert back to previous zoom if possible.
834  */
835 void
836 SPDesktop::prev_zoom()
838     if (zooms_past == NULL) {
839         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
840         return;
841     }
843     // push current zoom into forward zooms list
844     push_current_zoom (&zooms_future);
846     // restore previous zoom
847     set_display_area (((NRRect *) zooms_past->data)->x0,
848             ((NRRect *) zooms_past->data)->y0,
849             ((NRRect *) zooms_past->data)->x1,
850             ((NRRect *) zooms_past->data)->y1,
851             0, false);
853     // remove the just-added zoom from the past zooms list
854     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
857 /**
858  * Set zoom to next in list.
859  */
860 void
861 SPDesktop::next_zoom()
863     if (zooms_future == NULL) {
864         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
865         return;
866     }
868     // push current zoom into past zooms list
869     push_current_zoom (&zooms_past);
871     // restore next zoom
872     set_display_area (((NRRect *) zooms_future->data)->x0,
873             ((NRRect *) zooms_future->data)->y0,
874             ((NRRect *) zooms_future->data)->x1,
875             ((NRRect *) zooms_future->data)->y1,
876             0, false);
878     // remove the just-used zoom from the zooms_future list
879     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
882 /** \brief  Performs a quick zoom into what the user is working on
883     \param  enable  Whether we're going in or out of quick zoom
885 */
886 void
887 SPDesktop::zoom_quick (bool enable)
889     if (enable == _quick_zoom_enabled) {
890         return;
891     }
893     if (enable == true) {
894         _quick_zoom_stored_area = get_display_area();
895         bool zoomed = false;
897         // TODO This is brain damage. This needs to migrate into the node tool,
898         // but currently the design of this method is sufficiently broken
899         // to prevent this.
900         if (!zoomed && INK_IS_NODE_TOOL(event_context)) {
901             InkNodeTool *nt = static_cast<InkNodeTool*>(event_context);
902             if (!nt->_selected_nodes->empty()) {
903                 Geom::Rect nodes = *nt->_selected_nodes->bounds();
904                 double area = nodes.area();
905                 // do not zoom if a single cusp node is selected aand the bounds
906                 // have zero area.
907                 if (!Geom::are_near(area, 0) && area * 2.0 < _quick_zoom_stored_area.area()) {
908                     set_display_area(nodes, true);
909                     zoomed = true;
910                 }
911             }
912         }
914         if (!zoomed) {
915             Geom::OptRect const d = selection->bounds();
916             if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
917                 set_display_area(*d, true);
918                 zoomed = true;
919             }
920         }
922         if (!zoomed) {
923             zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
924             zoomed = true;
925         }
926     } else {
927         set_display_area(_quick_zoom_stored_area, false);
928     }
930     _quick_zoom_enabled = enable;
931     return;
934 /**
935  * Zoom to point with absolute zoom factor.
936  */
937 void
938 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
940     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
942     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
943     // this check prevents "sliding" when trying to zoom in at maximum zoom;
944     /// \todo someone please fix calculations properly and remove this hack
945     if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
946         return;
948     Geom::Rect const viewbox = canvas->getViewbox();
950     double const width2 = viewbox.dimensions()[Geom::X] / zoom;
951     double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
953     set_display_area(cx - px * width2,
954                      cy - py * height2,
955                      cx + (1 - px) * width2,
956                      cy + (1 - py) * height2,
957                      0.0);
960 /**
961  * Zoom to center with absolute zoom factor.
962  */
963 void
964 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
966     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
969 /**
970  * Zoom to point with relative zoom factor.
971  */
972 void
973 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
975     Geom::Rect const area = get_display_area();
977     if (cx < area.min()[Geom::X]) {
978         cx = area.min()[Geom::X];
979     }
980     if (cx > area.max()[Geom::X]) {
981         cx = area.max()[Geom::X];
982     }
983     if (cy < area.min()[Geom::Y]) {
984         cy = area.min()[Geom::Y];
985     }
986     if (cy > area.max()[Geom::Y]) {
987         cy = area.max()[Geom::Y];
988     }
990     gdouble const scale = _d2w.descrim() * zoom;
991     double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
992     double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
994     zoom_absolute_keep_point(cx, cy, px, py, scale);
997 /**
998  * Zoom to center with relative zoom factor.
999  */
1000 void
1001 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1003     gdouble scale = _d2w.descrim() * zoom;
1004     zoom_absolute (cx, cy, scale);
1007 /**
1008  * Set display area to origin and current document dimensions.
1009  */
1010 void
1011 SPDesktop::zoom_page()
1013     Geom::Rect d(Geom::Point(0, 0),
1014                  Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1016     if (d.minExtent() < 1.0) {
1017         return;
1018     }
1020     set_display_area(d, 10);
1023 /**
1024  * Set display area to current document width.
1025  */
1026 void
1027 SPDesktop::zoom_page_width()
1029     Geom::Rect const a = get_display_area();
1031     if (sp_document_width(doc()) < 1.0) {
1032         return;
1033     }
1035     Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1036                  Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1038     set_display_area(d, 10);
1041 /**
1042  * Zoom to selection.
1043  */
1044 void
1045 SPDesktop::zoom_selection()
1047     Geom::OptRect const d = selection->bounds();
1049     if ( !d || d->minExtent() < 0.1 ) {
1050         return;
1051     }
1053     set_display_area(*d, 10);
1056 /**
1057  * Tell widget to let zoom widget grab keyboard focus.
1058  */
1059 void
1060 SPDesktop::zoom_grab_focus()
1062     _widget->letZoomGrabFocus();
1065 /**
1066  * Zoom to whole drawing.
1067  */
1068 void
1069 SPDesktop::zoom_drawing()
1071     g_return_if_fail (doc() != NULL);
1072     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1073     g_return_if_fail (docitem != NULL);
1075     Geom::OptRect d = sp_item_bbox_desktop(docitem);
1077     /* Note that the second condition here indicates that
1078     ** there are no items in the drawing.
1079     */
1080     if ( !d || d->minExtent() < 0.1 ) {
1081         return;
1082     }
1084     set_display_area(*d, 10);
1087 /**
1088  * Scroll canvas by specific coordinate amount in svg coordinates.
1089  */
1090 void
1091 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1093     double scale = _d2w.descrim();
1094     scroll_world(dx*scale, dy*scale, is_scrolling);
1097 /**
1098  * Scroll canvas by specific coordinate amount.
1099  */
1100 void
1101 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1103     g_assert(_widget);
1105     Geom::Rect const viewbox = canvas->getViewbox();
1107     sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1109     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1110     sp_box3d_context_update_lines(event_context);
1112     _widget->updateRulers();
1113     _widget->updateScrollbars(_d2w.descrim());
1116 bool
1117 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1119     using Geom::X;
1120     using Geom::Y;
1122     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1123     gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1125     // autoscrolldistance is in screen pixels, but the display area is in document units
1126     autoscrolldistance /= _d2w.descrim();
1127     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1128     Geom::Rect dbox = get_display_area();
1129     dbox.expandBy(-autoscrolldistance);
1131     if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1132         !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y])   ) {
1134         Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1136         gdouble x_to;
1137         if (p[X] < dbox.min()[X])
1138             x_to = dbox.min()[X];
1139         else if (p[X] > dbox.max()[X])
1140             x_to = dbox.max()[X];
1141         else
1142             x_to = p[X];
1144         gdouble y_to;
1145         if (p[Y] < dbox.min()[Y])
1146             y_to = dbox.min()[Y];
1147         else if (p[Y] > dbox.max()[Y])
1148             y_to = dbox.max()[Y];
1149         else
1150             y_to = p[Y];
1152         Geom::Point const d_dt(x_to, y_to);
1153         Geom::Point const d_w( d_dt * _d2w );
1154         Geom::Point const moved_w( d_w - s_w );
1156         if (autoscrollspeed == 0)
1157             autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1159         if (autoscrollspeed != 0)
1160             scroll_world (autoscrollspeed * moved_w);
1162         return true;
1163     }
1164     return false;
1167 bool
1168 SPDesktop::is_iconified()
1170     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1173 void
1174 SPDesktop::iconify()
1176     _widget->setIconified();
1179 bool
1180 SPDesktop::is_maximized()
1182     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1185 void
1186 SPDesktop::maximize()
1188     _widget->setMaximized();
1191 bool
1192 SPDesktop::is_fullscreen()
1194     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1197 void
1198 SPDesktop::fullscreen()
1200     _widget->setFullscreen();
1203 /** \brief  Checks to see if the user is working in focused mode
1205     Returns the value of \c _focusMode
1206 */
1207 bool
1208 SPDesktop::is_focusMode()
1210     return _focusMode;
1213 /** \brief  Changes whether the user is in focus mode or not
1214     \param  mode  Which mode the view should be in
1216 */
1217 void
1218 SPDesktop::focusMode (bool mode)
1220     if (mode == _focusMode) { return; }
1222     _focusMode = mode;
1224     layoutWidget();
1225     //sp_desktop_widget_layout(SPDesktopWidget);
1227     return;
1230 void
1231 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1233     _widget->getGeometry (x, y, w, h);
1236 void
1237 SPDesktop::setWindowPosition (Geom::Point p)
1239     _widget->setPosition (p);
1242 void
1243 SPDesktop::setWindowSize (gint w, gint h)
1245     _widget->setSize (w, h);
1248 void
1249 SPDesktop::setWindowTransient (void *p, int transient_policy)
1251     _widget->setTransient (p, transient_policy);
1254 Gtk::Window*
1255 SPDesktop::getToplevel( )
1257     return _widget->getWindow();
1260 void
1261 SPDesktop::presentWindow()
1263     _widget->present();
1266 bool
1267 SPDesktop::warnDialog (gchar *text)
1269     return _widget->warnDialog (text);
1272 void
1273 SPDesktop::toggleRulers()
1275     _widget->toggleRulers();
1278 void
1279 SPDesktop::toggleScrollbars()
1281     _widget->toggleScrollbars();
1284 void
1285 SPDesktop::layoutWidget()
1287     _widget->layout();
1290 void
1291 SPDesktop::destroyWidget()
1293     _widget->destroy();
1296 bool
1297 SPDesktop::shutdown()
1299     return _widget->shutdown();
1302 bool SPDesktop::onDeleteUI (GdkEventAny*)
1304     if(shutdown())
1305         return true;
1307     destroyWidget();
1308     return false;
1311 /**
1312  *  onWindowStateEvent
1313  *
1314  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1315  *  Since GTK doesn't have a way to query this state information directly, we
1316  *  record it for the desktop here, and also possibly trigger a layout.
1317  */
1318 bool
1319 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1321     // Record the desktop window's state
1322     window_state = event->new_window_state;
1324     // Layout may differ depending on full-screen mode or not
1325     GdkWindowState changed = event->changed_mask;
1326     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1327         layoutWidget();
1328     }
1330     return false;
1333 void
1334 SPDesktop::setToolboxFocusTo (gchar const *label)
1336     _widget->setToolboxFocusTo (label);
1339 void
1340 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1342     _widget->setToolboxAdjustmentValue (id, val);
1345 void
1346 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1348     _widget->setToolboxSelectOneValue (id, val);
1351 bool
1352 SPDesktop::isToolboxButtonActive (gchar const *id)
1354     return _widget->isToolboxButtonActive (id);
1357 void
1358 SPDesktop::emitToolSubselectionChanged(gpointer data)
1360     _tool_subselection_changed.emit(data);
1361     inkscape_subselection_changed (this);
1364 void
1365 SPDesktop::updateNow()
1367   sp_canvas_update_now(canvas);
1370 void
1371 SPDesktop::enableInteraction()
1373   _widget->enableInteraction();
1376 void SPDesktop::disableInteraction()
1378   _widget->disableInteraction();
1381 void SPDesktop::setWaitingCursor()
1383     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1384     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1385     gdk_cursor_unref(waiting);
1386     // GDK needs the flush for the cursor change to take effect
1387     gdk_flush();
1388     waiting_cursor = true;
1391 void SPDesktop::clearWaitingCursor()
1393   if (waiting_cursor)
1394       sp_event_context_update_cursor(sp_desktop_event_context(this));
1397 void SPDesktop::toggleColorProfAdjust()
1399     _widget->toggleColorProfAdjust();
1402 void SPDesktop::toggleGrids()
1404     if (namedview->grids) {
1405         if(gridgroup) {
1406             showGrids(!grids_visible);
1407         }
1408     } else {
1409         //there is no grid present at the moment. add a rectangular grid and make it visible
1410         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1411         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1412         showGrids(true);
1413     }
1416 void SPDesktop::showGrids(bool show, bool dirty_document)
1418     grids_visible = show;
1419     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1420     if (show) {
1421         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1422     } else {
1423         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1424     }
1427 void SPDesktop::toggleSnapGlobal()
1429     bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1430     Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1431     sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1434 //----------------------------------------------------------------------
1435 // Callback implementations. The virtual ones are connected by the view.
1437 void
1438 SPDesktop::onPositionSet (double x, double y)
1440     _widget->viewSetPosition (Geom::Point(x,y));
1443 void
1444 SPDesktop::onResized (double /*x*/, double /*y*/)
1446    // Nothing called here
1449 /**
1450  * Redraw callback; queues Gtk redraw; connected by View.
1451  */
1452 void
1453 SPDesktop::onRedrawRequested ()
1455     if (main) {
1456         _widget->requestCanvasUpdate();
1457     }
1460 void
1461 SPDesktop::updateCanvasNow()
1463   _widget->requestCanvasUpdateAndWait();
1466 /**
1467  * Associate document with desktop.
1468  */
1469 void
1470 SPDesktop::setDocument (SPDocument *doc)
1472     if (this->doc() && doc) {
1473         namedview->hide(this);
1474         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1475     }
1477     if (_layer_hierarchy) {
1478         _layer_hierarchy->clear();
1479         delete _layer_hierarchy;
1480     }
1481     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1482     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1483     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1484     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1485     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1487     /* setup EventLog */
1488     event_log = new Inkscape::EventLog(doc);
1489     doc->addUndoObserver(*event_log);
1491     _commit_connection.disconnect();
1492     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1494     /// \todo fixme: This condition exists to make sure the code
1495     /// inside is NOT called on initialization, only on replacement. But there
1496     /// are surely more safe methods to accomplish this.
1497     // TODO since the comment had reversed logic, check the intent of this block of code:
1498     if (drawing) {
1499         NRArenaItem *ai = 0;
1501         namedview = sp_document_namedview (doc, NULL);
1502         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1503         number = namedview->getViewCount();
1505         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1506                 SP_CANVAS_ARENA (drawing)->arena,
1507                 dkey,
1508                 SP_ITEM_SHOW_DISPLAY);
1509         if (ai) {
1510             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1511         }
1512         namedview->show(this);
1513         /* Ugly hack */
1514         activate_guides (true);
1515         /* Ugly hack */
1516         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1517     }
1519     _document_replaced_signal.emit (this, doc);
1521     View::setDocument (doc);
1524 void
1525 SPDesktop::onStatusMessage
1526 (Inkscape::MessageType type, gchar const *message)
1528     if (_widget) {
1529         _widget->setMessage(type, message);
1530     }
1533 void
1534 SPDesktop::onDocumentURISet (gchar const* uri)
1536     _widget->setTitle(uri);
1539 /**
1540  * Resized callback.
1541  */
1542 void
1543 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1545     _doc2dt[5] = 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(SP_OBJECT_ID(desktop->currentLayer()));
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 :