Code

- fix bug #414142 (Moving path nodes twice is hard when snap is enabled)
[inkscape.git] / src / desktop.cpp
1 #define __SP_DESKTOP_C__
3 /** \file
4  * Editable view implementation
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   MenTaLguY <mental@rydia.net>
9  *   bulia byak <buliabyak@users.sf.net>
10  *   Ralf Stephan <ralf@ark.in-berlin.de>
11  *   John Bintz <jcoswell@coswellproductions.org>
12  *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
13  *
14  * Copyright (C) 2007 Jon A. Cruz
15  * Copyright (C) 2006-2008 Johan Engelen
16  * Copyright (C) 2006 John Bintz
17  * Copyright (C) 2004 MenTaLguY
18  * Copyright (C) 1999-2002 Lauris Kaplinski
19  * Copyright (C) 2000-2001 Ximian, Inc.
20  *
21  * Released under GNU GPL, read the file 'COPYING' for more information
22  */
24 /** \class SPDesktop
25  * SPDesktop is a subclass of View, implementing an editable document
26  * canvas.  It is extensively used by many UI controls that need certain
27  * visual representations of their own.
28  *
29  * SPDesktop provides a certain set of SPCanvasItems, serving as GUI
30  * layers of different control objects. The one containing the whole
31  * document is the drawing layer. In addition to it, there are grid,
32  * guide, sketch and control layers. The sketch layer is used for
33  * temporary drawing objects, before the real objects in document are
34  * created. The control layer contains editing knots, rubberband and
35  * similar non-document UI objects.
36  *
37  * Each SPDesktop is associated with a SPNamedView node of the document
38  * tree.  Currently, all desktops are created from a single main named
39  * view, but in the future there may be support for different ones.
40  * SPNamedView serves as an in-document container for desktop-related
41  * data, like grid and guideline placement, snapping options and so on.
42  *
43  * Associated with each SPDesktop are the two most important editing
44  * related objects - SPSelection and SPEventContext.
45  *
46  * Sodipodi keeps track of the active desktop and invokes notification
47  * signals whenever it changes. UI elements can use these to update their
48  * display to the selection of the currently active editing window.
49  * (Lauris Kaplinski)
50  */
52 #ifdef HAVE_CONFIG_H
53 # include "config.h"
54 #endif
56 #include <glibmm/i18n.h>
57 #include <sigc++/functors/mem_fun.h>
58 #include <gtkmm.h>
60 #include <2geom/rect.h>
61 #include "macros.h"
62 #include "inkscape-private.h"
63 #include "desktop.h"
64 #include "desktop-events.h"
65 #include "desktop-handles.h"
66 #include "document.h"
67 #include "message-stack.h"
68 #include "selection.h"
69 #include "select-context.h"
70 #include "sp-namedview.h"
71 #include "color.h"
72 #include "sp-item-group.h"
73 #include "preferences.h"
74 #include "object-hierarchy.h"
75 #include "helper/units.h"
76 #include "display/canvas-arena.h"
77 #include "display/nr-arena.h"
78 #include "display/gnome-canvas-acetate.h"
79 #include "display/sodipodi-ctrlrect.h"
80 #include "display/sp-canvas-util.h"
81 #include "display/canvas-temporary-item-list.h"
82 #include "display/snap-indicator.h"
83 #include "ui/dialog/dialog-manager.h"
84 #include "xml/repr.h"
85 #include "message-context.h"
86 #include "device-manager.h"
87 #include "layer-fns.h"
88 #include "layer-manager.h"
89 #include "event-log.h"
90 #include "display/canvas-grid.h"
91 #include "widgets/desktop-widget.h"
92 #include "box3d-context.h"
94 #include "display/sp-canvas.h"
96 namespace Inkscape { namespace XML { class Node; }}
98 // Callback declarations
99 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
100 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
101 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
102 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
103 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
104 static void _reconstruction_start(SPDesktop * desktop);
105 static void _reconstruction_finish(SPDesktop * desktop);
106 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
108 /**
109  * Return new desktop object.
110  * \pre namedview != NULL.
111  * \pre canvas != NULL.
112  */
113 SPDesktop::SPDesktop() :
114     _dlg_mgr( 0 ),
115     namedview( 0 ),
116     canvas( 0 ),
117     selection( 0 ),
118     event_context( 0 ),
119     layer_manager( 0 ),
120     event_log( 0 ),
121     temporary_item_list( 0 ),
122     snapindicator( 0 ),
123     acetate( 0 ),
124     main( 0 ),
125     gridgroup( 0 ),
126     guides( 0 ),
127     drawing( 0 ),
128     sketch( 0 ),
129     controls( 0 ),
130     tempgroup ( 0 ),
131     table( 0 ),
132     page( 0 ),
133     page_border( 0 ),
134     current( 0 ),
135     _focusMode(false),
136     zooms_past( 0 ),
137     zooms_future( 0 ),
138     dkey( 0 ),
139     number( 0 ),
140     window_state(0),
141     interaction_disabled_counter( 0 ),
142     waiting_cursor( false ),
143     guides_active( false ),
144     gr_item( 0 ),
145     gr_point_type( 0 ),
146     gr_point_i( 0 ),
147     gr_fill_or_stroke( true ),
148     _layer_hierarchy( 0 ),
149     _reconstruction_old_layer_id( 0 ),
150     _display_mode(Inkscape::RENDERMODE_NORMAL),
151     _saved_display_mode(Inkscape::RENDERMODE_NORMAL),
152     _widget( 0 ),
153     _inkscape( 0 ),
154     _guides_message_context( 0 ),
155     _active( false ),
156     _w2d(),
157     _d2w(),
158     _doc2dt( Geom::Scale(1, -1) ),
159     grids_visible( false )
161     _d2w.setIdentity();
162     _w2d.setIdentity();
164     selection = Inkscape::GC::release( new Inkscape::Selection(this) );
167 void
168 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
170     // Temporary workaround for link order issues:
171     Inkscape::DeviceManager::getManager().getDevices();
172     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
174     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
176     current = prefs->getStyle("/desktop/style");
178     namedview = nv;
179     canvas = aCanvas;
181     SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
182     /* Kill flicker */
183     sp_document_ensure_up_to_date (document);
185     /* Setup Dialog Manager */
186     _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
188     dkey = sp_item_display_key_new (1);
190     /* Connect document */
191     setDocument (document);
193     number = namedview->getViewCount();
196     /* Setup Canvas */
197     g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
199     SPCanvasGroup *root = sp_canvas_root (canvas);
201     /* Setup adminstrative layers */
202     acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
203     g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
204     main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
205     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
207     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
208     SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
209     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
210     sp_canvas_item_move_to_z (table, 0);
212     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
213     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
214     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
216     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
217     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
219     SP_CANVAS_ARENA (drawing)->arena->delta = prefs->getDouble("/options/cursortolerance/value", 1.0); // default is 1 px
221     if (prefs->getBool("/options/startmode/outline")) {
222         // Start in outline mode
223         setDisplayModeOutline();
224     } else {
225         // Start in normal mode, default
226         setDisplayModeNormal();
227     }
229     // The order in which these canvas items are added determines the z-order. It's therefore
230     // important to add the tempgroup (which will contain the snapindicator) before adding the
231     // controls. Only this way one will be able to quickly (before the snap indicator has
232     // disappeared) reselect a node after snapping it. If the z-order is wrong however, this
233     // will not work (the snap indicator is on top of the node handler; is the snapindicator
234     // being selected? or does it intercept some of the events that should have gone to the
235     // node handler? see bug https://bugs.launchpad.net/inkscape/+bug/414142)
236     gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
237     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
238     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
239     tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
240     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
242     /* Push select tool to the bottom of stack */
243     /** \todo
244      * FIXME: this is the only call to this.  Everything else seems to just
245      * call "set" instead of "push".  Can we assume that there is only one
246      * context ever?
247      */
248     push_event_context (SP_TYPE_SELECT_CONTEXT, "/tools/select", SP_EVENT_CONTEXT_STATIC);
250     // display rect and zoom are now handled in sp_desktop_widget_realize()
252     Geom::Rect const d(Geom::Point(0.0, 0.0),
253                        Geom::Point(sp_document_width(document), sp_document_height(document)));
255     SP_CTRLRECT(page)->setRectangle(d);
256     SP_CTRLRECT(page_border)->setRectangle(d);
258     /* the following sets the page shadow on the canvas
259        It was originally set to 5, which is really cheesy!
260        It now is an attribute in the document's namedview. If a value of
261        0 is used, then the constructor for a shadow is not initialized.
262     */
264     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
265         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
266     }
269     /* Connect event for page resize */
270     _doc2dt[5] = sp_document_height (document);
271     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
273     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
275     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
276             SP_CANVAS_ARENA (drawing)->arena,
277             dkey,
278             SP_ITEM_SHOW_DISPLAY);
279     if (ai) {
280         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
281     }
283     namedview->show(this);
284     /* Ugly hack */
285     activate_guides (true);
286     /* Ugly hack */
287     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
289 /* Set up notification of rebuilding the document, this allows
290        for saving object related settings in the document. */
291     _reconstruction_start_connection =
292         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
293     _reconstruction_finish_connection =
294         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
295     _reconstruction_old_layer_id = NULL;
297     // ?
298     // sp_active_desktop_set (desktop);
299     _inkscape = INKSCAPE;
301     _activate_connection = _activate_signal.connect(
302         sigc::bind(
303             sigc::ptr_fun(_onActivate),
304             this
305         )
306     );
307      _deactivate_connection = _deactivate_signal.connect(
308         sigc::bind(
309             sigc::ptr_fun(_onDeactivate),
310             this
311         )
312     );
314     _sel_modified_connection = selection->connectModified(
315         sigc::bind(
316             sigc::ptr_fun(&_onSelectionModified),
317             this
318         )
319     );
320     _sel_changed_connection = selection->connectChanged(
321         sigc::bind(
322             sigc::ptr_fun(&_onSelectionChanged),
323             this
324         )
325     );
328     /* setup LayerManager */
329     //   (Setting up after the connections are all in place, as it may use some of them)
330     layer_manager = new Inkscape::LayerManager( this );
332     showGrids(namedview->grids_visible, false);
334     temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
335     snapindicator = new Inkscape::Display::SnapIndicator ( this );
339 void SPDesktop::destroy()
341     if (snapindicator) {
342         delete snapindicator;
343         snapindicator = NULL;
344     }
345     if (temporary_item_list) {
346         delete temporary_item_list;
347         temporary_item_list = NULL;
348     }
350     if (selection) {
351         delete selection;
352         selection = NULL;
353     }
355     namedview->hide(this);
357     _activate_connection.disconnect();
358     _deactivate_connection.disconnect();
359     _sel_modified_connection.disconnect();
360     _sel_changed_connection.disconnect();
361     _modified_connection.disconnect();
362     _commit_connection.disconnect();
363     _reconstruction_start_connection.disconnect();
364     _reconstruction_finish_connection.disconnect();
366     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
367     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
368     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
370     while (event_context) {
371         SPEventContext *ec = event_context;
372         event_context = ec->next;
373         sp_event_context_finish (ec);
374         g_object_unref (G_OBJECT (ec));
375     }
377     if (_layer_hierarchy) {
378         delete _layer_hierarchy;
379 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
380     }
382     if (layer_manager) {
383         delete layer_manager;
384         layer_manager = NULL;
385     }
387     if (_inkscape) {
388         _inkscape = NULL;
389     }
391     if (drawing) {
392         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
393         drawing = NULL;
394     }
396     delete _guides_message_context;
397     _guides_message_context = NULL;
399     g_list_free (zooms_past);
400     g_list_free (zooms_future);
403 SPDesktop::~SPDesktop() {}
405 //--------------------------------------------------------------------
406 /* Public methods */
409 /* These methods help for temporarily showing things on-canvas.
410  * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
411  * is when you want to prematurely remove the item from the canvas, by calling
412  * desktop->remove_temporary_canvasitem(tempitem).
413  */
414 /** Note that lifetime is measured in milliseconds
415  * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
416  * delete the object for you and the reference will become invalid without you knowing it.
417  * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
418  * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
419  * because the object might be deleted already without you knowing it.
420  * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
421  */
422 Inkscape::Display::TemporaryItem *
423 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
425     if (move_to_bottom) {
426         sp_canvas_item_move_to_z(item, 0);
427     }
429     return temporary_item_list->add_item(item, lifetime);
432 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
433 */
434 void
435 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
437     // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
438     if (tempitem && temporary_item_list) {
439         temporary_item_list->delete_item(tempitem);
440     }
443 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
444     SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
445     canvas->rendermode = mode;
446     _display_mode = mode;
447     if (mode != Inkscape::RENDERMODE_OUTLINE) {
448         _saved_display_mode = _display_mode;
449     }
450     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
451     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
454 void SPDesktop::displayModeToggle() {
455     if (_display_mode == Inkscape::RENDERMODE_OUTLINE) {
456         _setDisplayMode(_saved_display_mode);
457     } else {
458         _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
459     }
462 /**
463  * Returns current root (=bottom) layer.
464  */
465 SPObject *SPDesktop::currentRoot() const
467     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
470 /**
471  * Returns current top layer.
472  */
473 SPObject *SPDesktop::currentLayer() const
475     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
478 /**
479  * Sets the current layer of the desktop.
480  *
481  * Make \a object the top layer.
482  */
483 void SPDesktop::setCurrentLayer(SPObject *object) {
484     g_return_if_fail(SP_IS_GROUP(object));
485     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
486     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
487     _layer_hierarchy->setBottom(object);
490 void SPDesktop::toggleLayerSolo(SPObject *object) {
491     g_return_if_fail(SP_IS_GROUP(object));
492     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
494     bool othersShowing = false;
495     std::vector<SPObject*> layers;
496     for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
497         layers.push_back(obj);
498         othersShowing |= !SP_ITEM(obj)->isHidden();
499     }
500     for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
501         layers.push_back(obj);
502         othersShowing |= !SP_ITEM(obj)->isHidden();
503     }
506     if ( SP_ITEM(object)->isHidden() ) {
507         SP_ITEM(object)->setHidden(false);
508     }
510     for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
511         SP_ITEM(*it)->setHidden(othersShowing);
512     }
515 /**
516  * Return layer that contains \a object.
517  */
518 SPObject *SPDesktop::layerForObject(SPObject *object) {
519     g_return_val_if_fail(object != NULL, NULL);
521     SPObject *root=currentRoot();
522     object = SP_OBJECT_PARENT(object);
523     while ( object && object != root && !isLayer(object) ) {
524         object = SP_OBJECT_PARENT(object);
525     }
526     return object;
529 /**
530  * True if object is a layer.
531  */
532 bool SPDesktop::isLayer(SPObject *object) const {
533     return ( SP_IS_GROUP(object)
534              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
535                   == SPGroup::LAYER ) );
538 /**
539  * True if desktop viewport fully contains \a item's bbox.
540  */
541 bool SPDesktop::isWithinViewport (SPItem *item) const
543     Geom::Rect const viewport = get_display_area();
544     Geom::OptRect const bbox = sp_item_bbox_desktop(item);
545     if (bbox) {
546         return viewport.contains(*bbox);
547     } else {
548         return true;
549     }
552 ///
553 bool SPDesktop::itemIsHidden(SPItem const *item) const {
554     return item->isHidden(this->dkey);
557 /**
558  * Set activate property of desktop; emit signal if changed.
559  */
560 void
561 SPDesktop::set_active (bool new_active)
563     if (new_active != _active) {
564         _active = new_active;
565         if (new_active) {
566             _activate_signal.emit();
567         } else {
568             _deactivate_signal.emit();
569         }
570     }
573 /**
574  * Set activate status of current desktop's named view.
575  */
576 void
577 SPDesktop::activate_guides(bool activate)
579     guides_active = activate;
580     namedview->activateGuides(this, activate);
583 /**
584  * Make desktop switch documents.
585  */
586 void
587 SPDesktop::change_document (SPDocument *theDocument)
589     g_return_if_fail (theDocument != NULL);
591     /* unselect everything before switching documents */
592     selection->clear();
594     setDocument (theDocument);
596     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
597        (this can probably be done in a better way) */
598     Gtk::Window *parent = this->getToplevel();
599     g_assert(parent != NULL);
600     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
601     if (dtw) dtw->desktop = this;
602     sp_desktop_widget_update_namedview(dtw);
604     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
605     _document_replaced_signal.emit (this, theDocument);
608 /**
609  * Make desktop switch event contexts.
610  */
611 void
612 SPDesktop::set_event_context (GtkType type, const gchar *config)
614     SPEventContext *ec;
615     while (event_context) {
616         ec = event_context;
617         sp_event_context_deactivate (ec);
618         event_context = ec->next;
619         sp_event_context_finish (ec);
620         g_object_unref (G_OBJECT (ec));
621     }
623     ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
624     ec->next = event_context;
625     event_context = ec;
626     sp_event_context_activate (ec);
627     _event_context_changed_signal.emit (this, ec);
630 /**
631  * Push event context onto desktop's context stack.
632  */
633 void
634 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
636     SPEventContext *ref, *ec;
638     if (event_context && event_context->key == key) return;
639     ref = event_context;
640     while (ref && ref->next && ref->next->key != key) ref = ref->next;
641     if (ref && ref->next) {
642         ec = ref->next;
643         ref->next = ec->next;
644         sp_event_context_finish (ec);
645         g_object_unref (G_OBJECT (ec));
646     }
648     if (event_context) sp_event_context_deactivate (event_context);
649     ec = sp_event_context_new (type, this, config, key);
650     ec->next = event_context;
651     event_context = ec;
652     sp_event_context_activate (ec);
653     _event_context_changed_signal.emit (this, ec);
656 /**
657  * Sets the coordinate status to a given point
658  */
659 void
660 SPDesktop::set_coordinate_status (Geom::Point p) {
661     _widget->setCoordinateStatus(p);
664 /**
665  * \see sp_document_item_from_list_at_point_bottom()
666  */
667 SPItem *
668 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
670     g_return_val_if_fail (doc() != NULL, NULL);
671     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
674 /**
675  * \see sp_document_item_at_point()
676  */
677 SPItem *
678 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
680     g_return_val_if_fail (doc() != NULL, NULL);
681     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
684 /**
685  * \see sp_document_group_at_point()
686  */
687 SPItem *
688 SPDesktop::group_at_point (Geom::Point const p) const
690     g_return_val_if_fail (doc() != NULL, NULL);
691     return sp_document_group_at_point (doc(), dkey, p);
694 /**
695  * \brief  Returns the mouse point in document coordinates; if mouse is
696  * outside the canvas, returns the center of canvas viewpoint
697  */
698 Geom::Point
699 SPDesktop::point() const
701     Geom::Point p = _widget->getPointer();
702     Geom::Point pw = sp_canvas_window_to_world (canvas, p);
703     p = w2d(pw);
705     Geom::Rect const r = canvas->getViewbox();
707     Geom::Point r0 = w2d(r.min());
708     Geom::Point r1 = w2d(r.max());
710     if (p[Geom::X] >= r0[Geom::X] &&
711         p[Geom::X] <= r1[Geom::X] &&
712         p[Geom::Y] >= r1[Geom::Y] &&
713         p[Geom::Y] <= r0[Geom::Y])
714     {
715         return p;
716     } else {
717         return (r0 + r1) / 2;
718     }
721 /**
722  * Put current zoom data in history list.
723  */
724 void
725 SPDesktop::push_current_zoom (GList **history)
727     Geom::Rect const area = get_display_area();
729     NRRect *old_zoom = g_new(NRRect, 1);
730     old_zoom->x0 = area.min()[Geom::X];
731     old_zoom->x1 = area.max()[Geom::X];
732     old_zoom->y0 = area.min()[Geom::Y];
733     old_zoom->y1 = area.max()[Geom::Y];
734     if ( *history == NULL
735          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
736                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
737                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
738                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
739     {
740         *history = g_list_prepend (*history, old_zoom);
741     }
744 /**
745  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
746  */
747 void
748 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
750     g_assert(_widget);
752     // save the zoom
753     if (log) {
754         push_current_zoom(&zooms_past);
755         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
756         g_list_free (zooms_future);
757         zooms_future = NULL;
758     }
760     double const cx = 0.5 * (x0 + x1);
761     double const cy = 0.5 * (y0 + y1);
763     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
764     Geom::Rect viewbox = canvas->getViewbox();
765     viewbox.expandBy(-border);
767     double scale = _d2w.descrim();
768     double newscale;
769     if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
770         newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
771     } else {
772         newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
773     }
775     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
777     int clear = FALSE;
778     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
779         /* Set zoom factors */
780         _d2w = Geom::Scale(newscale, -newscale);
781         _w2d = Geom::Scale(1/newscale, 1/-newscale);
782         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
783         clear = TRUE;
784     }
786     /* Calculate top left corner (in document pixels) */
787     x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
788     y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
790     /* Scroll */
791     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
793     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
794     sp_box3d_context_update_lines(event_context);
796     _widget->updateRulers();
797     _widget->updateScrollbars(_d2w.descrim());
798     _widget->updateZoom();
801 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
803     set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
806 /**
807  * Return viewbox dimensions.
808  */
809 Geom::Rect SPDesktop::get_display_area() const
811     Geom::Rect const viewbox = canvas->getViewbox();
813     double const scale = _d2w[0];
815     return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
816                       Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
819 /**
820  * Revert back to previous zoom if possible.
821  */
822 void
823 SPDesktop::prev_zoom()
825     if (zooms_past == NULL) {
826         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
827         return;
828     }
830     // push current zoom into forward zooms list
831     push_current_zoom (&zooms_future);
833     // restore previous zoom
834     set_display_area (((NRRect *) zooms_past->data)->x0,
835             ((NRRect *) zooms_past->data)->y0,
836             ((NRRect *) zooms_past->data)->x1,
837             ((NRRect *) zooms_past->data)->y1,
838             0, false);
840     // remove the just-added zoom from the past zooms list
841     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
844 /**
845  * Set zoom to next in list.
846  */
847 void
848 SPDesktop::next_zoom()
850     if (zooms_future == NULL) {
851         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
852         return;
853     }
855     // push current zoom into past zooms list
856     push_current_zoom (&zooms_past);
858     // restore next zoom
859     set_display_area (((NRRect *) zooms_future->data)->x0,
860             ((NRRect *) zooms_future->data)->y0,
861             ((NRRect *) zooms_future->data)->x1,
862             ((NRRect *) zooms_future->data)->y1,
863             0, false);
865     // remove the just-used zoom from the zooms_future list
866     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
869 #include "tools-switch.h"
870 #include "node-context.h"
871 #include "shape-editor.h"
872 #include "nodepath.h"
874 /** \brief  Performs a quick zoom into what the user is working on
875     \param  enable  Whether we're going in or out of quick zoom
877 */
878 void
879 SPDesktop::zoom_quick (bool enable)
881     if (enable == _quick_zoom_enabled) {
882         return;
883     }
885     if (enable == true) {
886         _quick_zoom_stored_area = get_display_area();
887         bool zoomed = false;
889         if (!zoomed) {
890             SPItem * singleItem = selection->singleItem();
891             if (singleItem != NULL && tools_isactive(this, TOOLS_NODES)) {
893                 Inkscape::NodePath::Path * nodepath = event_context->shape_editor->get_nodepath();
894                 // printf("I've got a nodepath, crazy\n");
896                                 if (nodepath) {
897                                         Geom::Rect nodes;
898                                         bool firstnode = true;
900                                         if (nodepath->selected) {
901                                                 for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
902                                                    Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
903                                                         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
904                                                            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
905                                                                 if (node->selected) {
906                                                                         // printf("\tSelected node\n");
907                                                                         if (firstnode) {
908                                                                                 nodes = Geom::Rect(node->pos, node->pos);
909                                                                                 firstnode = false;
910                                                                         } else {
911                                                                                 nodes.expandTo(node->pos);
912                                                                         }
914                                                                         if (node->p.other != NULL) {
915                                                                                 /* Include previous node pos */
916                                                                                 nodes.expandTo(node->p.other->pos);
918                                                                                 /* Include previous handle */
919                                                                                 if (!sp_node_side_is_line(node, &node->p)) {
920                                                                                         nodes.expandTo(node->p.pos);
921                                                                                 }
922                                                                         }
924                                                                         if (node->n.other != NULL) {
925                                                                                 /* Include previous node pos */
926                                                                                 nodes.expandTo(node->n.other->pos);
928                                                                                 /* Include previous handle */
929                                                                                 if (!sp_node_side_is_line(node, &node->n)) {
930                                                                                         nodes.expandTo(node->n.pos);
931                                                                                 }
932                                                                         }
933                                                                 }
934                                                         }
935                                                 }
937                                                 if (!firstnode && nodes.area() * 2.0 < _quick_zoom_stored_area.area()) {
938                                                         set_display_area(nodes, 10);
939                                                         zoomed = true;
940                                                 }
941                                         }
942                                 }
943             }
944         }
946         if (!zoomed) {
947             Geom::OptRect const d = selection->bounds();
948             if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
949                 set_display_area(*d, 10);
950                 zoomed = true;
951             }
952         }
954         if (!zoomed) {
955             zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
956             zoomed = true;
957         }
958     } else {
959         set_display_area(_quick_zoom_stored_area, 0);
960     }
962     _quick_zoom_enabled = enable;
963     return;
966 /**
967  * Zoom to point with absolute zoom factor.
968  */
969 void
970 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
972     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
974     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
975     // this check prevents "sliding" when trying to zoom in at maximum zoom;
976     /// \todo someone please fix calculations properly and remove this hack
977     if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
978         return;
980     Geom::Rect const viewbox = canvas->getViewbox();
982     double const width2 = viewbox.dimensions()[Geom::X] / zoom;
983     double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
985     set_display_area(cx - px * width2,
986                      cy - py * height2,
987                      cx + (1 - px) * width2,
988                      cy + (1 - py) * height2,
989                      0.0);
992 /**
993  * Zoom to center with absolute zoom factor.
994  */
995 void
996 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
998     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
1001 /**
1002  * Zoom to point with relative zoom factor.
1003  */
1004 void
1005 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
1007     Geom::Rect const area = get_display_area();
1009     if (cx < area.min()[Geom::X]) {
1010         cx = area.min()[Geom::X];
1011     }
1012     if (cx > area.max()[Geom::X]) {
1013         cx = area.max()[Geom::X];
1014     }
1015     if (cy < area.min()[Geom::Y]) {
1016         cy = area.min()[Geom::Y];
1017     }
1018     if (cy > area.max()[Geom::Y]) {
1019         cy = area.max()[Geom::Y];
1020     }
1022     gdouble const scale = _d2w.descrim() * zoom;
1023     double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
1024     double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
1026     zoom_absolute_keep_point(cx, cy, px, py, scale);
1029 /**
1030  * Zoom to center with relative zoom factor.
1031  */
1032 void
1033 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1035     gdouble scale = _d2w.descrim() * zoom;
1036     zoom_absolute (cx, cy, scale);
1039 /**
1040  * Set display area to origin and current document dimensions.
1041  */
1042 void
1043 SPDesktop::zoom_page()
1045     Geom::Rect d(Geom::Point(0, 0),
1046                  Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1048     if (d.minExtent() < 1.0) {
1049         return;
1050     }
1052     set_display_area(d, 10);
1055 /**
1056  * Set display area to current document width.
1057  */
1058 void
1059 SPDesktop::zoom_page_width()
1061     Geom::Rect const a = get_display_area();
1063     if (sp_document_width(doc()) < 1.0) {
1064         return;
1065     }
1067     Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1068                  Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1070     set_display_area(d, 10);
1073 /**
1074  * Zoom to selection.
1075  */
1076 void
1077 SPDesktop::zoom_selection()
1079     Geom::OptRect const d = selection->bounds();
1081     if ( !d || d->minExtent() < 0.1 ) {
1082         return;
1083     }
1085     set_display_area(*d, 10);
1088 /**
1089  * Tell widget to let zoom widget grab keyboard focus.
1090  */
1091 void
1092 SPDesktop::zoom_grab_focus()
1094     _widget->letZoomGrabFocus();
1097 /**
1098  * Zoom to whole drawing.
1099  */
1100 void
1101 SPDesktop::zoom_drawing()
1103     g_return_if_fail (doc() != NULL);
1104     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1105     g_return_if_fail (docitem != NULL);
1107     Geom::OptRect d = sp_item_bbox_desktop(docitem);
1109     /* Note that the second condition here indicates that
1110     ** there are no items in the drawing.
1111     */
1112     if ( !d || d->minExtent() < 0.1 ) {
1113         return;
1114     }
1116     set_display_area(*d, 10);
1119 /**
1120  * Scroll canvas by specific coordinate amount in svg coordinates.
1121  */
1122 void
1123 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1125     double scale = _d2w.descrim();
1126     scroll_world(dx*scale, dy*scale, is_scrolling);
1129 /**
1130  * Scroll canvas by specific coordinate amount.
1131  */
1132 void
1133 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1135     g_assert(_widget);
1137     Geom::Rect const viewbox = canvas->getViewbox();
1139     sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1141     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1142     sp_box3d_context_update_lines(event_context);
1144     _widget->updateRulers();
1145     _widget->updateScrollbars(_d2w.descrim());
1148 bool
1149 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1151     using Geom::X;
1152     using Geom::Y;
1154     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1155     gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1157     // autoscrolldistance is in screen pixels, but the display area is in document units
1158     autoscrolldistance /= _d2w.descrim();
1159     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1160     Geom::Rect dbox = get_display_area();
1161     dbox.expandBy(-autoscrolldistance);
1163     if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1164         !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y])   ) {
1166         Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1168         gdouble x_to;
1169         if (p[X] < dbox.min()[X])
1170             x_to = dbox.min()[X];
1171         else if (p[X] > dbox.max()[X])
1172             x_to = dbox.max()[X];
1173         else
1174             x_to = p[X];
1176         gdouble y_to;
1177         if (p[Y] < dbox.min()[Y])
1178             y_to = dbox.min()[Y];
1179         else if (p[Y] > dbox.max()[Y])
1180             y_to = dbox.max()[Y];
1181         else
1182             y_to = p[Y];
1184         Geom::Point const d_dt(x_to, y_to);
1185         Geom::Point const d_w( d_dt * _d2w );
1186         Geom::Point const moved_w( d_w - s_w );
1188         if (autoscrollspeed == 0)
1189             autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1191         if (autoscrollspeed != 0)
1192             scroll_world (autoscrollspeed * moved_w);
1194         return true;
1195     }
1196     return false;
1199 bool
1200 SPDesktop::is_iconified()
1202     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1205 void
1206 SPDesktop::iconify()
1208     _widget->setIconified();
1211 bool
1212 SPDesktop::is_maximized()
1214     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1217 void
1218 SPDesktop::maximize()
1220     _widget->setMaximized();
1223 bool
1224 SPDesktop::is_fullscreen()
1226     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1229 void
1230 SPDesktop::fullscreen()
1232     _widget->setFullscreen();
1235 /** \brief  Checks to see if the user is working in focused mode
1237     Returns the value of \c _focusMode
1238 */
1239 bool
1240 SPDesktop::is_focusMode()
1242     return _focusMode;
1245 /** \brief  Changes whether the user is in focus mode or not
1246     \param  mode  Which mode the view should be in
1248 */
1249 void
1250 SPDesktop::focusMode (bool mode)
1252     if (mode == _focusMode) { return; }
1254     _focusMode = mode;
1256     layoutWidget();
1257     //sp_desktop_widget_layout(SPDesktopWidget);
1259     return;
1262 void
1263 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1265     _widget->getGeometry (x, y, w, h);
1268 void
1269 SPDesktop::setWindowPosition (Geom::Point p)
1271     _widget->setPosition (p);
1274 void
1275 SPDesktop::setWindowSize (gint w, gint h)
1277     _widget->setSize (w, h);
1280 void
1281 SPDesktop::setWindowTransient (void *p, int transient_policy)
1283     _widget->setTransient (p, transient_policy);
1286 Gtk::Window*
1287 SPDesktop::getToplevel( )
1289     return _widget->getWindow();
1292 void
1293 SPDesktop::presentWindow()
1295     _widget->present();
1298 bool
1299 SPDesktop::warnDialog (gchar *text)
1301     return _widget->warnDialog (text);
1304 void
1305 SPDesktop::toggleRulers()
1307     _widget->toggleRulers();
1310 void
1311 SPDesktop::toggleScrollbars()
1313     _widget->toggleScrollbars();
1316 void
1317 SPDesktop::layoutWidget()
1319     _widget->layout();
1322 void
1323 SPDesktop::destroyWidget()
1325     _widget->destroy();
1328 bool
1329 SPDesktop::shutdown()
1331     return _widget->shutdown();
1334 bool SPDesktop::onDeleteUI (GdkEventAny*)
1336     if(shutdown())
1337         return true;
1339     destroyWidget();
1340     return false;
1343 /**
1344  *  onWindowStateEvent
1345  *
1346  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1347  *  Since GTK doesn't have a way to query this state information directly, we
1348  *  record it for the desktop here, and also possibly trigger a layout.
1349  */
1350 bool
1351 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1353     // Record the desktop window's state
1354     window_state = event->new_window_state;
1356     // Layout may differ depending on full-screen mode or not
1357     GdkWindowState changed = event->changed_mask;
1358     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1359         layoutWidget();
1360     }
1362     return false;
1365 void
1366 SPDesktop::setToolboxFocusTo (gchar const *label)
1368     _widget->setToolboxFocusTo (label);
1371 void
1372 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1374     _widget->setToolboxAdjustmentValue (id, val);
1377 void
1378 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1380     _widget->setToolboxSelectOneValue (id, val);
1383 bool
1384 SPDesktop::isToolboxButtonActive (gchar const *id)
1386     return _widget->isToolboxButtonActive (id);
1389 void
1390 SPDesktop::emitToolSubselectionChanged(gpointer data)
1392     _tool_subselection_changed.emit(data);
1393     inkscape_subselection_changed (this);
1396 void
1397 SPDesktop::updateNow()
1399   sp_canvas_update_now(canvas);
1402 void
1403 SPDesktop::enableInteraction()
1405   _widget->enableInteraction();
1408 void SPDesktop::disableInteraction()
1410   _widget->disableInteraction();
1413 void SPDesktop::setWaitingCursor()
1415     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1416     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1417     gdk_cursor_unref(waiting);
1418     // GDK needs the flush for the cursor change to take effect
1419     gdk_flush();
1420     waiting_cursor = true;
1423 void SPDesktop::clearWaitingCursor()
1425   if (waiting_cursor)
1426       sp_event_context_update_cursor(sp_desktop_event_context(this));
1429 void SPDesktop::toggleColorProfAdjust()
1431     _widget->toggleColorProfAdjust();
1434 void SPDesktop::toggleGrids()
1436     if (namedview->grids) {
1437         if(gridgroup) {
1438             showGrids(!grids_visible);
1439         }
1440     } else {
1441         //there is no grid present at the moment. add a rectangular grid and make it visible
1442         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1443         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1444         showGrids(true);
1445     }
1448 void SPDesktop::showGrids(bool show, bool dirty_document)
1450     grids_visible = show;
1451     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1452     if (show) {
1453         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1454     } else {
1455         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1456     }
1459 void SPDesktop::toggleSnapGlobal()
1461     bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1462     Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1463     sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1466 //----------------------------------------------------------------------
1467 // Callback implementations. The virtual ones are connected by the view.
1469 void
1470 SPDesktop::onPositionSet (double x, double y)
1472     _widget->viewSetPosition (Geom::Point(x,y));
1475 void
1476 SPDesktop::onResized (double /*x*/, double /*y*/)
1478    // Nothing called here
1481 /**
1482  * Redraw callback; queues Gtk redraw; connected by View.
1483  */
1484 void
1485 SPDesktop::onRedrawRequested ()
1487     if (main) {
1488         _widget->requestCanvasUpdate();
1489     }
1492 void
1493 SPDesktop::updateCanvasNow()
1495   _widget->requestCanvasUpdateAndWait();
1498 /**
1499  * Associate document with desktop.
1500  */
1501 void
1502 SPDesktop::setDocument (SPDocument *doc)
1504     if (this->doc() && doc) {
1505         namedview->hide(this);
1506         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1507     }
1509     if (_layer_hierarchy) {
1510         _layer_hierarchy->clear();
1511         delete _layer_hierarchy;
1512     }
1513     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1514     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1515     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1516     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1517     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1519     /* setup EventLog */
1520     event_log = new Inkscape::EventLog(doc);
1521     doc->addUndoObserver(*event_log);
1523     _commit_connection.disconnect();
1524     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1526     /// \todo fixme: This condition exists to make sure the code
1527     /// inside is NOT called on initialization, only on replacement. But there
1528     /// are surely more safe methods to accomplish this.
1529     // TODO since the comment had reversed logic, check the intent of this block of code:
1530     if (drawing) {
1531         NRArenaItem *ai = 0;
1533         namedview = sp_document_namedview (doc, NULL);
1534         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1535         number = namedview->getViewCount();
1537         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1538                 SP_CANVAS_ARENA (drawing)->arena,
1539                 dkey,
1540                 SP_ITEM_SHOW_DISPLAY);
1541         if (ai) {
1542             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1543         }
1544         namedview->show(this);
1545         /* Ugly hack */
1546         activate_guides (true);
1547         /* Ugly hack */
1548         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1549     }
1551     _document_replaced_signal.emit (this, doc);
1553     View::setDocument (doc);
1556 void
1557 SPDesktop::onStatusMessage
1558 (Inkscape::MessageType type, gchar const *message)
1560     if (_widget) {
1561         _widget->setMessage(type, message);
1562     }
1565 void
1566 SPDesktop::onDocumentURISet (gchar const* uri)
1568     _widget->setTitle(uri);
1571 /**
1572  * Resized callback.
1573  */
1574 void
1575 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1577     _doc2dt[5] = height;
1578     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1579     Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1580     SP_CTRLRECT(page)->setRectangle(a);
1581     SP_CTRLRECT(page_border)->setRectangle(a);
1585 void
1586 SPDesktop::_onActivate (SPDesktop* dt)
1588     if (!dt->_widget) return;
1589     dt->_widget->activateDesktop();
1592 void
1593 SPDesktop::_onDeactivate (SPDesktop* dt)
1595     if (!dt->_widget) return;
1596     dt->_widget->deactivateDesktop();
1599 void
1600 SPDesktop::_onSelectionModified
1601 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1603     if (!dt->_widget) return;
1604     dt->_widget->updateScrollbars (dt->_d2w.descrim());
1607 static void
1608 _onSelectionChanged
1609 (Inkscape::Selection *selection, SPDesktop *desktop)
1611     /** \todo
1612      * only change the layer for single selections, or what?
1613      * This seems reasonable -- for multiple selections there can be many
1614      * different layers involved.
1615      */
1616     SPItem *item=selection->singleItem();
1617     if (item) {
1618         SPObject *layer=desktop->layerForObject(item);
1619         if ( layer && layer != desktop->currentLayer() ) {
1620             desktop->setCurrentLayer(layer);
1621         }
1622     }
1625 /**
1626  * Calls event handler of current event context.
1627  * \param arena Unused
1628  * \todo fixme
1629  */
1630 static gint
1631 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1633     if (ai) {
1634         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1635         return sp_event_context_item_handler (desktop->event_context, spi, event);
1636     } else {
1637         return sp_event_context_root_handler (desktop->event_context, event);
1638     }
1641 static void
1642 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1643     g_return_if_fail(SP_IS_GROUP(layer));
1644     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1647 /// Callback
1648 static void
1649 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1650     g_return_if_fail(SP_IS_GROUP(layer));
1651     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1654 /// Callback
1655 static void
1656 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1657                                          SPDesktop *desktop)
1659     desktop->_layer_changed_signal.emit (bottom);
1662 /// Called when document is starting to be rebuilt.
1663 static void
1664 _reconstruction_start (SPDesktop * desktop)
1666     // printf("Desktop, starting reconstruction\n");
1667     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1668     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1670     /*
1671     GSList const * selection_objs = desktop->selection->list();
1672     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1674     }
1675     */
1676     desktop->selection->clear();
1678     // printf("Desktop, starting reconstruction end\n");
1681 /// Called when document rebuild is finished.
1682 static void
1683 _reconstruction_finish (SPDesktop * desktop)
1685     // printf("Desktop, finishing reconstruction\n");
1686     if (desktop->_reconstruction_old_layer_id == NULL)
1687         return;
1689     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1690     if (newLayer != NULL)
1691         desktop->setCurrentLayer(newLayer);
1693     g_free(desktop->_reconstruction_old_layer_id);
1694     desktop->_reconstruction_old_layer_id = NULL;
1695     // printf("Desktop, finishing reconstruction end\n");
1696     return;
1699 /**
1700  * Namedview_modified callback.
1701  */
1702 static void
1703 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1705     SPNamedView *nv=SP_NAMEDVIEW(obj);
1707     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1709         /* Show/hide page background */
1710         if (nv->pagecolor & 0xff) {
1711             sp_canvas_item_show (desktop->table);
1712             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1713             sp_canvas_item_move_to_z (desktop->table, 0);
1714         } else {
1715             sp_canvas_item_hide (desktop->table);
1716         }
1718         /* Show/hide page border */
1719         if (nv->showborder) {
1720             // show
1721             sp_canvas_item_show (desktop->page_border);
1722             // set color and shadow
1723             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1724             if (nv->pageshadow) {
1725                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1726             }
1727             // place in the z-order stack
1728             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1729                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1730             } else {
1731                 int order = sp_canvas_item_order (desktop->page_border);
1732                 int morder = sp_canvas_item_order (desktop->drawing);
1733                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1734                     morder - order);
1735             }
1736         } else {
1737                 sp_canvas_item_hide (desktop->page_border);
1738                 if (nv->pageshadow) {
1739                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1740                 }
1741         }
1743         /* Show/hide page shadow */
1744         if (nv->showpageshadow && nv->pageshadow) {
1745             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1746         } else {
1747             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1748         }
1750         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1751         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1752             (SP_RGBA32_R_U(nv->pagecolor) +
1753              SP_RGBA32_G_U(nv->pagecolor) +
1754              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1755             // the background color is light or transparent, use black outline
1756             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1757         } else { // use white outline
1758             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1759         }
1760     }
1763 Geom::Matrix SPDesktop::w2d() const
1765     return _w2d;
1768 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1770     return p * _w2d;
1773 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1775     return p * _d2w;
1778 Geom::Matrix SPDesktop::doc2dt() const
1780     return _doc2dt;
1783 Geom::Matrix SPDesktop::dt2doc() const
1785     // doc2dt is its own inverse
1786     return _doc2dt;
1789 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1791     return p * _doc2dt;
1794 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1796     return p * dt2doc();
1800 /**
1801  * Pop event context from desktop's context stack. Never used.
1802  */
1803 // void
1804 // SPDesktop::pop_event_context (unsigned int key)
1805 // {
1806 //    SPEventContext *ec = NULL;
1807 //
1808 //    if (event_context && event_context->key == key) {
1809 //        g_return_if_fail (event_context);
1810 //        g_return_if_fail (event_context->next);
1811 //        ec = event_context;
1812 //        sp_event_context_deactivate (ec);
1813 //        event_context = ec->next;
1814 //        sp_event_context_activate (event_context);
1815 //        _event_context_changed_signal.emit (this, ec);
1816 //    }
1817 //
1818 //    SPEventContext *ref = event_context;
1819 //    while (ref && ref->next && ref->next->key != key)
1820 //        ref = ref->next;
1821 //
1822 //    if (ref && ref->next) {
1823 //        ec = ref->next;
1824 //        ref->next = ec->next;
1825 //    }
1826 //
1827 //    if (ec) {
1828 //        sp_event_context_finish (ec);
1829 //        g_object_unref (G_OBJECT (ec));
1830 //    }
1831 // }
1833 /*
1834   Local Variables:
1835   mode:c++
1836   c-file-style:"stroustrup"
1837   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1838   indent-tabs-mode:nil
1839   fill-column:99
1840   End:
1841 */
1842 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :