Code

Updating to current trunk
[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     _widget( 0 ),
152     _inkscape( 0 ),
153     _guides_message_context( 0 ),
154     _active( false ),
155     _w2d(),
156     _d2w(),
157     _doc2dt( Geom::Scale(1, -1) ),
158     grids_visible( false )
160     _d2w.setIdentity();
161     _w2d.setIdentity();
163     selection = Inkscape::GC::release( new Inkscape::Selection(this) );
166 void
167 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas, Inkscape::UI::View::EditWidgetInterface *widget)
169     _widget = widget;
171     // Temporary workaround for link order issues:
172     Inkscape::DeviceManager::getManager().getDevices();
173     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
175     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
177     current = prefs->getStyle("/desktop/style");
179     namedview = nv;
180     canvas = aCanvas;
182     SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
183     /* Kill flicker */
184     sp_document_ensure_up_to_date (document);
186     /* Setup Dialog Manager */
187     _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
189     dkey = sp_item_display_key_new (1);
191     /* Connect document */
192     setDocument (document);
194     number = namedview->getViewCount();
197     /* Setup Canvas */
198     g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
200     SPCanvasGroup *root = sp_canvas_root (canvas);
202     /* Setup adminstrative layers */
203     acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
204     g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
205     main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
206     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
208     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
209     SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
210     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
211     sp_canvas_item_move_to_z (table, 0);
213     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
214     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
215     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
217     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
218     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
220     SP_CANVAS_ARENA (drawing)->arena->delta = prefs->getDouble("/options/cursortolerance/value", 1.0); // default is 1 px
222     if (prefs->getBool("/options/startmode/outline")) {
223         // Start in outline mode
224         setDisplayModeOutline();
225     } else {
226         // Start in normal mode, default
227         setDisplayModeNormal();
228     }
230     // The order in which these canvas items are added determines the z-order. It's therefore
231     // important to add the tempgroup (which will contain the snapindicator) before adding the
232     // controls. Only this way one will be able to quickly (before the snap indicator has
233     // disappeared) reselect a node after snapping it. If the z-order is wrong however, this
234     // will not work (the snap indicator is on top of the node handler; is the snapindicator
235     // being selected? or does it intercept some of the events that should have gone to the
236     // node handler? see bug https://bugs.launchpad.net/inkscape/+bug/414142)
237     gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
238     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
239     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
240     tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
241     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
243     /* Push select tool to the bottom of stack */
244     /** \todo
245      * FIXME: this is the only call to this.  Everything else seems to just
246      * call "set" instead of "push".  Can we assume that there is only one
247      * context ever?
248      */
249     push_event_context (SP_TYPE_SELECT_CONTEXT, "/tools/select", SP_EVENT_CONTEXT_STATIC);
251     // display rect and zoom are now handled in sp_desktop_widget_realize()
253     Geom::Rect const d(Geom::Point(0.0, 0.0),
254                        Geom::Point(sp_document_width(document), sp_document_height(document)));
256     SP_CTRLRECT(page)->setRectangle(d);
257     SP_CTRLRECT(page_border)->setRectangle(d);
259     /* the following sets the page shadow on the canvas
260        It was originally set to 5, which is really cheesy!
261        It now is an attribute in the document's namedview. If a value of
262        0 is used, then the constructor for a shadow is not initialized.
263     */
265     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
266         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
267     }
270     /* Connect event for page resize */
271     _doc2dt[5] = sp_document_height (document);
272     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
274     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
276     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
277             SP_CANVAS_ARENA (drawing)->arena,
278             dkey,
279             SP_ITEM_SHOW_DISPLAY);
280     if (ai) {
281         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
282     }
284     namedview->show(this);
285     /* Ugly hack */
286     activate_guides (true);
287     /* Ugly hack */
288     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
290 /* Set up notification of rebuilding the document, this allows
291        for saving object related settings in the document. */
292     _reconstruction_start_connection =
293         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
294     _reconstruction_finish_connection =
295         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
296     _reconstruction_old_layer_id = NULL;
298     // ?
299     // sp_active_desktop_set (desktop);
300     _inkscape = INKSCAPE;
302     _activate_connection = _activate_signal.connect(
303         sigc::bind(
304             sigc::ptr_fun(_onActivate),
305             this
306         )
307     );
308      _deactivate_connection = _deactivate_signal.connect(
309         sigc::bind(
310             sigc::ptr_fun(_onDeactivate),
311             this
312         )
313     );
315     _sel_modified_connection = selection->connectModified(
316         sigc::bind(
317             sigc::ptr_fun(&_onSelectionModified),
318             this
319         )
320     );
321     _sel_changed_connection = selection->connectChanged(
322         sigc::bind(
323             sigc::ptr_fun(&_onSelectionChanged),
324             this
325         )
326     );
329     /* setup LayerManager */
330     //   (Setting up after the connections are all in place, as it may use some of them)
331     layer_manager = new Inkscape::LayerManager( this );
333     showGrids(namedview->grids_visible, false);
335     temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
336     snapindicator = new Inkscape::Display::SnapIndicator ( this );
340 void SPDesktop::destroy()
342     if (snapindicator) {
343         delete snapindicator;
344         snapindicator = NULL;
345     }
346     if (temporary_item_list) {
347         delete temporary_item_list;
348         temporary_item_list = NULL;
349     }
351     if (selection) {
352         delete selection;
353         selection = NULL;
354     }
356     namedview->hide(this);
358     _activate_connection.disconnect();
359     _deactivate_connection.disconnect();
360     _sel_modified_connection.disconnect();
361     _sel_changed_connection.disconnect();
362     _modified_connection.disconnect();
363     _commit_connection.disconnect();
364     _reconstruction_start_connection.disconnect();
365     _reconstruction_finish_connection.disconnect();
367     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
368     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
369     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
371     while (event_context) {
372         SPEventContext *ec = event_context;
373         event_context = ec->next;
374         sp_event_context_finish (ec);
375         g_object_unref (G_OBJECT (ec));
376     }
378     if (_layer_hierarchy) {
379         delete _layer_hierarchy;
380 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
381     }
383     if (layer_manager) {
384         delete layer_manager;
385         layer_manager = NULL;
386     }
388     if (_inkscape) {
389         _inkscape = NULL;
390     }
392     if (drawing) {
393         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
394         drawing = NULL;
395     }
397     delete _guides_message_context;
398     _guides_message_context = NULL;
400     g_list_free (zooms_past);
401     g_list_free (zooms_future);
404 SPDesktop::~SPDesktop() {}
406 //--------------------------------------------------------------------
407 /* Public methods */
410 /* These methods help for temporarily showing things on-canvas.
411  * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
412  * is when you want to prematurely remove the item from the canvas, by calling
413  * desktop->remove_temporary_canvasitem(tempitem).
414  */
415 /** Note that lifetime is measured in milliseconds
416  * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
417  * delete the object for you and the reference will become invalid without you knowing it.
418  * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
419  * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
420  * because the object might be deleted already without you knowing it.
421  * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
422  */
423 Inkscape::Display::TemporaryItem *
424 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
426     if (move_to_bottom) {
427         sp_canvas_item_move_to_z(item, 0);
428     }
430     return temporary_item_list->add_item(item, lifetime);
433 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
434 */
435 void
436 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
438     // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
439     if (tempitem && temporary_item_list) {
440         temporary_item_list->delete_item(tempitem);
441     }
444 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
445     SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
446     canvas->rendermode = mode;
447     _display_mode = mode;
448     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
449     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
452 void SPDesktop::displayModeToggle() {
453     switch (_display_mode) {
454     case Inkscape::RENDERMODE_NORMAL:
455         _setDisplayMode(Inkscape::RENDERMODE_NO_FILTERS);
456         break;
457     case Inkscape::RENDERMODE_NO_FILTERS:
458         _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
459         break;
460     case Inkscape::RENDERMODE_OUTLINE:
461     default:
462         _setDisplayMode(Inkscape::RENDERMODE_NORMAL);
463     }
466 /**
467  * Returns current root (=bottom) layer.
468  */
469 SPObject *SPDesktop::currentRoot() const
471     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
474 /**
475  * Returns current top layer.
476  */
477 SPObject *SPDesktop::currentLayer() const
479     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
482 /**
483  * Sets the current layer of the desktop.
484  *
485  * Make \a object the top layer.
486  */
487 void SPDesktop::setCurrentLayer(SPObject *object) {
488     g_return_if_fail(SP_IS_GROUP(object));
489     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
490     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
491     _layer_hierarchy->setBottom(object);
494 void SPDesktop::toggleLayerSolo(SPObject *object) {
495     g_return_if_fail(SP_IS_GROUP(object));
496     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
498     bool othersShowing = false;
499     std::vector<SPObject*> layers;
500     for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
501         layers.push_back(obj);
502         othersShowing |= !SP_ITEM(obj)->isHidden();
503     }
504     for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
505         layers.push_back(obj);
506         othersShowing |= !SP_ITEM(obj)->isHidden();
507     }
510     if ( SP_ITEM(object)->isHidden() ) {
511         SP_ITEM(object)->setHidden(false);
512     }
514     for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
515         SP_ITEM(*it)->setHidden(othersShowing);
516     }
519 /**
520  * Return layer that contains \a object.
521  */
522 SPObject *SPDesktop::layerForObject(SPObject *object) {
523     g_return_val_if_fail(object != NULL, NULL);
525     SPObject *root=currentRoot();
526     object = SP_OBJECT_PARENT(object);
527     while ( object && object != root && !isLayer(object) ) {
528         object = SP_OBJECT_PARENT(object);
529     }
530     return object;
533 /**
534  * True if object is a layer.
535  */
536 bool SPDesktop::isLayer(SPObject *object) const {
537     return ( SP_IS_GROUP(object)
538              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
539                   == SPGroup::LAYER ) );
542 /**
543  * True if desktop viewport fully contains \a item's bbox.
544  */
545 bool SPDesktop::isWithinViewport (SPItem *item) const
547     Geom::Rect const viewport = get_display_area();
548     Geom::OptRect const bbox = sp_item_bbox_desktop(item);
549     if (bbox) {
550         return viewport.contains(*bbox);
551     } else {
552         return true;
553     }
556 ///
557 bool SPDesktop::itemIsHidden(SPItem const *item) const {
558     return item->isHidden(this->dkey);
561 /**
562  * Set activate property of desktop; emit signal if changed.
563  */
564 void
565 SPDesktop::set_active (bool new_active)
567     if (new_active != _active) {
568         _active = new_active;
569         if (new_active) {
570             _activate_signal.emit();
571         } else {
572             _deactivate_signal.emit();
573         }
574     }
577 /**
578  * Set activate status of current desktop's named view.
579  */
580 void
581 SPDesktop::activate_guides(bool activate)
583     guides_active = activate;
584     namedview->activateGuides(this, activate);
587 /**
588  * Make desktop switch documents.
589  */
590 void
591 SPDesktop::change_document (SPDocument *theDocument)
593     g_return_if_fail (theDocument != NULL);
595     /* unselect everything before switching documents */
596     selection->clear();
598     setDocument (theDocument);
600     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
601        (this can probably be done in a better way) */
602     Gtk::Window *parent = this->getToplevel();
603     g_assert(parent != NULL);
604     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
605     if (dtw) dtw->desktop = this;
606     sp_desktop_widget_update_namedview(dtw);
608     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
609     _document_replaced_signal.emit (this, theDocument);
612 /**
613  * Make desktop switch event contexts.
614  */
615 void
616 SPDesktop::set_event_context (GtkType type, const gchar *config)
618     SPEventContext *ec;
619     while (event_context) {
620         ec = event_context;
621         sp_event_context_deactivate (ec);
622         event_context = ec->next;
623         sp_event_context_finish (ec);
624         g_object_unref (G_OBJECT (ec));
625     }
627     ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
628     ec->next = event_context;
629     event_context = ec;
630     sp_event_context_activate (ec);
631     _event_context_changed_signal.emit (this, ec);
634 /**
635  * Push event context onto desktop's context stack.
636  */
637 void
638 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
640     SPEventContext *ref, *ec;
642     if (event_context && event_context->key == key) return;
643     ref = event_context;
644     while (ref && ref->next && ref->next->key != key) ref = ref->next;
645     if (ref && ref->next) {
646         ec = ref->next;
647         ref->next = ec->next;
648         sp_event_context_finish (ec);
649         g_object_unref (G_OBJECT (ec));
650     }
652     if (event_context) sp_event_context_deactivate (event_context);
653     ec = sp_event_context_new (type, this, config, key);
654     ec->next = event_context;
655     event_context = ec;
656     sp_event_context_activate (ec);
657     _event_context_changed_signal.emit (this, ec);
660 /**
661  * Sets the coordinate status to a given point
662  */
663 void
664 SPDesktop::set_coordinate_status (Geom::Point p) {
665     _widget->setCoordinateStatus(p);
668 /**
669  * \see sp_document_item_from_list_at_point_bottom()
670  */
671 SPItem *
672 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
674     g_return_val_if_fail (doc() != NULL, NULL);
675     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
678 /**
679  * \see sp_document_item_at_point()
680  */
681 SPItem *
682 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
684     g_return_val_if_fail (doc() != NULL, NULL);
685     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
688 /**
689  * \see sp_document_group_at_point()
690  */
691 SPItem *
692 SPDesktop::group_at_point (Geom::Point const p) const
694     g_return_val_if_fail (doc() != NULL, NULL);
695     return sp_document_group_at_point (doc(), dkey, p);
698 /**
699  * \brief  Returns the mouse point in document coordinates; if mouse is
700  * outside the canvas, returns the center of canvas viewpoint
701  */
702 Geom::Point
703 SPDesktop::point() const
705     Geom::Point p = _widget->getPointer();
706     Geom::Point pw = sp_canvas_window_to_world (canvas, p);
707     p = w2d(pw);
709     Geom::Rect const r = canvas->getViewbox();
711     Geom::Point r0 = w2d(r.min());
712     Geom::Point r1 = w2d(r.max());
714     if (p[Geom::X] >= r0[Geom::X] &&
715         p[Geom::X] <= r1[Geom::X] &&
716         p[Geom::Y] >= r1[Geom::Y] &&
717         p[Geom::Y] <= r0[Geom::Y])
718     {
719         return p;
720     } else {
721         return (r0 + r1) / 2;
722     }
725 /**
726  * Put current zoom data in history list.
727  */
728 void
729 SPDesktop::push_current_zoom (GList **history)
731     Geom::Rect const area = get_display_area();
733     NRRect *old_zoom = g_new(NRRect, 1);
734     old_zoom->x0 = area.min()[Geom::X];
735     old_zoom->x1 = area.max()[Geom::X];
736     old_zoom->y0 = area.min()[Geom::Y];
737     old_zoom->y1 = area.max()[Geom::Y];
738     if ( *history == NULL
739          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
740                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
741                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
742                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
743     {
744         *history = g_list_prepend (*history, old_zoom);
745     }
748 /**
749  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
750  */
751 void
752 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
754     g_assert(_widget);
756     // save the zoom
757     if (log) {
758         push_current_zoom(&zooms_past);
759         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
760         g_list_free (zooms_future);
761         zooms_future = NULL;
762     }
764     double const cx = 0.5 * (x0 + x1);
765     double const cy = 0.5 * (y0 + y1);
767     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
768     Geom::Rect viewbox = canvas->getViewbox();
769     viewbox.expandBy(-border);
771     double scale = _d2w.descrim();
772     double newscale;
773     if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
774         newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
775     } else {
776         newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
777     }
779     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
781     int clear = FALSE;
782     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
783         /* Set zoom factors */
784         _d2w = Geom::Scale(newscale, -newscale);
785         _w2d = Geom::Scale(1/newscale, 1/-newscale);
786         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
787         clear = TRUE;
788     }
790     /* Calculate top left corner (in document pixels) */
791     x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
792     y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
794     /* Scroll */
795     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
797     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
798     sp_box3d_context_update_lines(event_context);
800     _widget->updateRulers();
801     _widget->updateScrollbars(_d2w.descrim());
802     _widget->updateZoom();
805 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
807     set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
810 /**
811  * Return viewbox dimensions.
812  */
813 Geom::Rect SPDesktop::get_display_area() const
815     Geom::Rect const viewbox = canvas->getViewbox();
817     double const scale = _d2w[0];
819     return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
820                       Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
823 /**
824  * Revert back to previous zoom if possible.
825  */
826 void
827 SPDesktop::prev_zoom()
829     if (zooms_past == NULL) {
830         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
831         return;
832     }
834     // push current zoom into forward zooms list
835     push_current_zoom (&zooms_future);
837     // restore previous zoom
838     set_display_area (((NRRect *) zooms_past->data)->x0,
839             ((NRRect *) zooms_past->data)->y0,
840             ((NRRect *) zooms_past->data)->x1,
841             ((NRRect *) zooms_past->data)->y1,
842             0, false);
844     // remove the just-added zoom from the past zooms list
845     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
848 /**
849  * Set zoom to next in list.
850  */
851 void
852 SPDesktop::next_zoom()
854     if (zooms_future == NULL) {
855         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
856         return;
857     }
859     // push current zoom into past zooms list
860     push_current_zoom (&zooms_past);
862     // restore next zoom
863     set_display_area (((NRRect *) zooms_future->data)->x0,
864             ((NRRect *) zooms_future->data)->y0,
865             ((NRRect *) zooms_future->data)->x1,
866             ((NRRect *) zooms_future->data)->y1,
867             0, false);
869     // remove the just-used zoom from the zooms_future list
870     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
873 #include "tools-switch.h"
874 #include "node-context.h"
875 #include "shape-editor.h"
876 #include "nodepath.h"
878 /** \brief  Performs a quick zoom into what the user is working on
879     \param  enable  Whether we're going in or out of quick zoom
881 */
882 void
883 SPDesktop::zoom_quick (bool enable)
885     if (enable == _quick_zoom_enabled) {
886         return;
887     }
889     if (enable == true) {
890         _quick_zoom_stored_area = get_display_area();
891         bool zoomed = false;
893         if (!zoomed) {
894             SPItem * singleItem = selection->singleItem();
895             if (singleItem != NULL && tools_isactive(this, TOOLS_NODES)) {
897                 Inkscape::NodePath::Path * nodepath = event_context->shape_editor->get_nodepath();
898                 // printf("I've got a nodepath, crazy\n");
900                                 if (nodepath) {
901                                         Geom::Rect nodes;
902                                         bool firstnode = true;
904                                         if (nodepath->selected) {
905                                                 for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
906                                                    Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
907                                                         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
908                                                            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
909                                                                 if (node->selected) {
910                                                                         // printf("\tSelected node\n");
911                                                                         if (firstnode) {
912                                                                                 nodes = Geom::Rect(node->pos, node->pos);
913                                                                                 firstnode = false;
914                                                                         } else {
915                                                                                 nodes.expandTo(node->pos);
916                                                                         }
918                                                                         if (node->p.other != NULL) {
919                                                                                 /* Include previous node pos */
920                                                                                 nodes.expandTo(node->p.other->pos);
922                                                                                 /* Include previous handle */
923                                                                                 if (!sp_node_side_is_line(node, &node->p)) {
924                                                                                         nodes.expandTo(node->p.pos);
925                                                                                 }
926                                                                         }
928                                                                         if (node->n.other != NULL) {
929                                                                                 /* Include previous node pos */
930                                                                                 nodes.expandTo(node->n.other->pos);
932                                                                                 /* Include previous handle */
933                                                                                 if (!sp_node_side_is_line(node, &node->n)) {
934                                                                                         nodes.expandTo(node->n.pos);
935                                                                                 }
936                                                                         }
937                                                                 }
938                                                         }
939                                                 }
941                                                 if (!firstnode && nodes.area() * 2.0 < _quick_zoom_stored_area.area()) {
942                                                         set_display_area(nodes, 10);
943                                                         zoomed = true;
944                                                 }
945                                         }
946                                 }
947             }
948         }
950         if (!zoomed) {
951             Geom::OptRect const d = selection->bounds();
952             if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
953                 set_display_area(*d, 10);
954                 zoomed = true;
955             }
956         }
958         if (!zoomed) {
959             zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
960             zoomed = true;
961         }
962     } else {
963         set_display_area(_quick_zoom_stored_area, 0);
964     }
966     _quick_zoom_enabled = enable;
967     return;
970 /**
971  * Zoom to point with absolute zoom factor.
972  */
973 void
974 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
976     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
978     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
979     // this check prevents "sliding" when trying to zoom in at maximum zoom;
980     /// \todo someone please fix calculations properly and remove this hack
981     if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
982         return;
984     Geom::Rect const viewbox = canvas->getViewbox();
986     double const width2 = viewbox.dimensions()[Geom::X] / zoom;
987     double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
989     set_display_area(cx - px * width2,
990                      cy - py * height2,
991                      cx + (1 - px) * width2,
992                      cy + (1 - py) * height2,
993                      0.0);
996 /**
997  * Zoom to center with absolute zoom factor.
998  */
999 void
1000 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
1002     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
1005 /**
1006  * Zoom to point with relative zoom factor.
1007  */
1008 void
1009 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
1011     Geom::Rect const area = get_display_area();
1013     if (cx < area.min()[Geom::X]) {
1014         cx = area.min()[Geom::X];
1015     }
1016     if (cx > area.max()[Geom::X]) {
1017         cx = area.max()[Geom::X];
1018     }
1019     if (cy < area.min()[Geom::Y]) {
1020         cy = area.min()[Geom::Y];
1021     }
1022     if (cy > area.max()[Geom::Y]) {
1023         cy = area.max()[Geom::Y];
1024     }
1026     gdouble const scale = _d2w.descrim() * zoom;
1027     double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
1028     double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
1030     zoom_absolute_keep_point(cx, cy, px, py, scale);
1033 /**
1034  * Zoom to center with relative zoom factor.
1035  */
1036 void
1037 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1039     gdouble scale = _d2w.descrim() * zoom;
1040     zoom_absolute (cx, cy, scale);
1043 /**
1044  * Set display area to origin and current document dimensions.
1045  */
1046 void
1047 SPDesktop::zoom_page()
1049     Geom::Rect d(Geom::Point(0, 0),
1050                  Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1052     if (d.minExtent() < 1.0) {
1053         return;
1054     }
1056     set_display_area(d, 10);
1059 /**
1060  * Set display area to current document width.
1061  */
1062 void
1063 SPDesktop::zoom_page_width()
1065     Geom::Rect const a = get_display_area();
1067     if (sp_document_width(doc()) < 1.0) {
1068         return;
1069     }
1071     Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1072                  Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1074     set_display_area(d, 10);
1077 /**
1078  * Zoom to selection.
1079  */
1080 void
1081 SPDesktop::zoom_selection()
1083     Geom::OptRect const d = selection->bounds();
1085     if ( !d || d->minExtent() < 0.1 ) {
1086         return;
1087     }
1089     set_display_area(*d, 10);
1092 /**
1093  * Tell widget to let zoom widget grab keyboard focus.
1094  */
1095 void
1096 SPDesktop::zoom_grab_focus()
1098     _widget->letZoomGrabFocus();
1101 /**
1102  * Zoom to whole drawing.
1103  */
1104 void
1105 SPDesktop::zoom_drawing()
1107     g_return_if_fail (doc() != NULL);
1108     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1109     g_return_if_fail (docitem != NULL);
1111     Geom::OptRect d = sp_item_bbox_desktop(docitem);
1113     /* Note that the second condition here indicates that
1114     ** there are no items in the drawing.
1115     */
1116     if ( !d || d->minExtent() < 0.1 ) {
1117         return;
1118     }
1120     set_display_area(*d, 10);
1123 /**
1124  * Scroll canvas by specific coordinate amount in svg coordinates.
1125  */
1126 void
1127 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1129     double scale = _d2w.descrim();
1130     scroll_world(dx*scale, dy*scale, is_scrolling);
1133 /**
1134  * Scroll canvas by specific coordinate amount.
1135  */
1136 void
1137 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1139     g_assert(_widget);
1141     Geom::Rect const viewbox = canvas->getViewbox();
1143     sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1145     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1146     sp_box3d_context_update_lines(event_context);
1148     _widget->updateRulers();
1149     _widget->updateScrollbars(_d2w.descrim());
1152 bool
1153 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1155     using Geom::X;
1156     using Geom::Y;
1158     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1159     gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1161     // autoscrolldistance is in screen pixels, but the display area is in document units
1162     autoscrolldistance /= _d2w.descrim();
1163     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1164     Geom::Rect dbox = get_display_area();
1165     dbox.expandBy(-autoscrolldistance);
1167     if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1168         !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y])   ) {
1170         Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1172         gdouble x_to;
1173         if (p[X] < dbox.min()[X])
1174             x_to = dbox.min()[X];
1175         else if (p[X] > dbox.max()[X])
1176             x_to = dbox.max()[X];
1177         else
1178             x_to = p[X];
1180         gdouble y_to;
1181         if (p[Y] < dbox.min()[Y])
1182             y_to = dbox.min()[Y];
1183         else if (p[Y] > dbox.max()[Y])
1184             y_to = dbox.max()[Y];
1185         else
1186             y_to = p[Y];
1188         Geom::Point const d_dt(x_to, y_to);
1189         Geom::Point const d_w( d_dt * _d2w );
1190         Geom::Point const moved_w( d_w - s_w );
1192         if (autoscrollspeed == 0)
1193             autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1195         if (autoscrollspeed != 0)
1196             scroll_world (autoscrollspeed * moved_w);
1198         return true;
1199     }
1200     return false;
1203 bool
1204 SPDesktop::is_iconified()
1206     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1209 void
1210 SPDesktop::iconify()
1212     _widget->setIconified();
1215 bool
1216 SPDesktop::is_maximized()
1218     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1221 void
1222 SPDesktop::maximize()
1224     _widget->setMaximized();
1227 bool
1228 SPDesktop::is_fullscreen()
1230     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1233 void
1234 SPDesktop::fullscreen()
1236     _widget->setFullscreen();
1239 /** \brief  Checks to see if the user is working in focused mode
1241     Returns the value of \c _focusMode
1242 */
1243 bool
1244 SPDesktop::is_focusMode()
1246     return _focusMode;
1249 /** \brief  Changes whether the user is in focus mode or not
1250     \param  mode  Which mode the view should be in
1252 */
1253 void
1254 SPDesktop::focusMode (bool mode)
1256     if (mode == _focusMode) { return; }
1258     _focusMode = mode;
1260     layoutWidget();
1261     //sp_desktop_widget_layout(SPDesktopWidget);
1263     return;
1266 void
1267 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1269     _widget->getGeometry (x, y, w, h);
1272 void
1273 SPDesktop::setWindowPosition (Geom::Point p)
1275     _widget->setPosition (p);
1278 void
1279 SPDesktop::setWindowSize (gint w, gint h)
1281     _widget->setSize (w, h);
1284 void
1285 SPDesktop::setWindowTransient (void *p, int transient_policy)
1287     _widget->setTransient (p, transient_policy);
1290 Gtk::Window*
1291 SPDesktop::getToplevel( )
1293     return _widget->getWindow();
1296 void
1297 SPDesktop::presentWindow()
1299     _widget->present();
1302 bool
1303 SPDesktop::warnDialog (gchar *text)
1305     return _widget->warnDialog (text);
1308 void
1309 SPDesktop::toggleRulers()
1311     _widget->toggleRulers();
1314 void
1315 SPDesktop::toggleScrollbars()
1317     _widget->toggleScrollbars();
1320 void
1321 SPDesktop::layoutWidget()
1323     _widget->layout();
1326 void
1327 SPDesktop::destroyWidget()
1329     _widget->destroy();
1332 bool
1333 SPDesktop::shutdown()
1335     return _widget->shutdown();
1338 bool SPDesktop::onDeleteUI (GdkEventAny*)
1340     if(shutdown())
1341         return true;
1343     destroyWidget();
1344     return false;
1347 /**
1348  *  onWindowStateEvent
1349  *
1350  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1351  *  Since GTK doesn't have a way to query this state information directly, we
1352  *  record it for the desktop here, and also possibly trigger a layout.
1353  */
1354 bool
1355 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1357     // Record the desktop window's state
1358     window_state = event->new_window_state;
1360     // Layout may differ depending on full-screen mode or not
1361     GdkWindowState changed = event->changed_mask;
1362     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1363         layoutWidget();
1364     }
1366     return false;
1369 void
1370 SPDesktop::setToolboxFocusTo (gchar const *label)
1372     _widget->setToolboxFocusTo (label);
1375 void
1376 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1378     _widget->setToolboxAdjustmentValue (id, val);
1381 void
1382 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1384     _widget->setToolboxSelectOneValue (id, val);
1387 bool
1388 SPDesktop::isToolboxButtonActive (gchar const *id)
1390     return _widget->isToolboxButtonActive (id);
1393 void
1394 SPDesktop::emitToolSubselectionChanged(gpointer data)
1396     _tool_subselection_changed.emit(data);
1397     inkscape_subselection_changed (this);
1400 void
1401 SPDesktop::updateNow()
1403   sp_canvas_update_now(canvas);
1406 void
1407 SPDesktop::enableInteraction()
1409   _widget->enableInteraction();
1412 void SPDesktop::disableInteraction()
1414   _widget->disableInteraction();
1417 void SPDesktop::setWaitingCursor()
1419     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1420     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1421     gdk_cursor_unref(waiting);
1422     // GDK needs the flush for the cursor change to take effect
1423     gdk_flush();
1424     waiting_cursor = true;
1427 void SPDesktop::clearWaitingCursor()
1429   if (waiting_cursor)
1430       sp_event_context_update_cursor(sp_desktop_event_context(this));
1433 void SPDesktop::toggleColorProfAdjust()
1435     _widget->toggleColorProfAdjust();
1438 void SPDesktop::toggleGrids()
1440     if (namedview->grids) {
1441         if(gridgroup) {
1442             showGrids(!grids_visible);
1443         }
1444     } else {
1445         //there is no grid present at the moment. add a rectangular grid and make it visible
1446         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1447         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1448         showGrids(true);
1449     }
1452 void SPDesktop::showGrids(bool show, bool dirty_document)
1454     grids_visible = show;
1455     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1456     if (show) {
1457         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1458     } else {
1459         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1460     }
1463 void SPDesktop::toggleSnapGlobal()
1465     bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1466     Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1467     sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1470 //----------------------------------------------------------------------
1471 // Callback implementations. The virtual ones are connected by the view.
1473 void
1474 SPDesktop::onPositionSet (double x, double y)
1476     _widget->viewSetPosition (Geom::Point(x,y));
1479 void
1480 SPDesktop::onResized (double /*x*/, double /*y*/)
1482    // Nothing called here
1485 /**
1486  * Redraw callback; queues Gtk redraw; connected by View.
1487  */
1488 void
1489 SPDesktop::onRedrawRequested ()
1491     if (main) {
1492         _widget->requestCanvasUpdate();
1493     }
1496 void
1497 SPDesktop::updateCanvasNow()
1499   _widget->requestCanvasUpdateAndWait();
1502 /**
1503  * Associate document with desktop.
1504  */
1505 void
1506 SPDesktop::setDocument (SPDocument *doc)
1508     if (this->doc() && doc) {
1509         namedview->hide(this);
1510         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1511     }
1513     if (_layer_hierarchy) {
1514         _layer_hierarchy->clear();
1515         delete _layer_hierarchy;
1516     }
1517     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1518     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1519     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1520     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1521     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1523     /* setup EventLog */
1524     event_log = new Inkscape::EventLog(doc);
1525     doc->addUndoObserver(*event_log);
1527     _commit_connection.disconnect();
1528     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1530     /// \todo fixme: This condition exists to make sure the code
1531     /// inside is NOT called on initialization, only on replacement. But there
1532     /// are surely more safe methods to accomplish this.
1533     // TODO since the comment had reversed logic, check the intent of this block of code:
1534     if (drawing) {
1535         NRArenaItem *ai = 0;
1537         namedview = sp_document_namedview (doc, NULL);
1538         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1539         number = namedview->getViewCount();
1541         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1542                 SP_CANVAS_ARENA (drawing)->arena,
1543                 dkey,
1544                 SP_ITEM_SHOW_DISPLAY);
1545         if (ai) {
1546             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1547         }
1548         namedview->show(this);
1549         /* Ugly hack */
1550         activate_guides (true);
1551         /* Ugly hack */
1552         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1553     }
1555     _document_replaced_signal.emit (this, doc);
1557     View::setDocument (doc);
1560 void
1561 SPDesktop::onStatusMessage
1562 (Inkscape::MessageType type, gchar const *message)
1564     if (_widget) {
1565         _widget->setMessage(type, message);
1566     }
1569 void
1570 SPDesktop::onDocumentURISet (gchar const* uri)
1572     _widget->setTitle(uri);
1575 /**
1576  * Resized callback.
1577  */
1578 void
1579 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1581     _doc2dt[5] = height;
1582     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1583     Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1584     SP_CTRLRECT(page)->setRectangle(a);
1585     SP_CTRLRECT(page_border)->setRectangle(a);
1589 void
1590 SPDesktop::_onActivate (SPDesktop* dt)
1592     if (!dt->_widget) return;
1593     dt->_widget->activateDesktop();
1596 void
1597 SPDesktop::_onDeactivate (SPDesktop* dt)
1599     if (!dt->_widget) return;
1600     dt->_widget->deactivateDesktop();
1603 void
1604 SPDesktop::_onSelectionModified
1605 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1607     if (!dt->_widget) return;
1608     dt->_widget->updateScrollbars (dt->_d2w.descrim());
1611 static void
1612 _onSelectionChanged
1613 (Inkscape::Selection *selection, SPDesktop *desktop)
1615     /** \todo
1616      * only change the layer for single selections, or what?
1617      * This seems reasonable -- for multiple selections there can be many
1618      * different layers involved.
1619      */
1620     SPItem *item=selection->singleItem();
1621     if (item) {
1622         SPObject *layer=desktop->layerForObject(item);
1623         if ( layer && layer != desktop->currentLayer() ) {
1624             desktop->setCurrentLayer(layer);
1625         }
1626     }
1629 /**
1630  * Calls event handler of current event context.
1631  * \param arena Unused
1632  * \todo fixme
1633  */
1634 static gint
1635 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1637     if (ai) {
1638         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1639         return sp_event_context_item_handler (desktop->event_context, spi, event);
1640     } else {
1641         return sp_event_context_root_handler (desktop->event_context, event);
1642     }
1645 static void
1646 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1647     g_return_if_fail(SP_IS_GROUP(layer));
1648     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1651 /// Callback
1652 static void
1653 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1654     g_return_if_fail(SP_IS_GROUP(layer));
1655     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1658 /// Callback
1659 static void
1660 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1661                                          SPDesktop *desktop)
1663     desktop->_layer_changed_signal.emit (bottom);
1666 /// Called when document is starting to be rebuilt.
1667 static void
1668 _reconstruction_start (SPDesktop * desktop)
1670     // printf("Desktop, starting reconstruction\n");
1671     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1672     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1674     /*
1675     GSList const * selection_objs = desktop->selection->list();
1676     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1678     }
1679     */
1680     desktop->selection->clear();
1682     // printf("Desktop, starting reconstruction end\n");
1685 /// Called when document rebuild is finished.
1686 static void
1687 _reconstruction_finish (SPDesktop * desktop)
1689     // printf("Desktop, finishing reconstruction\n");
1690     if (desktop->_reconstruction_old_layer_id == NULL)
1691         return;
1693     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1694     if (newLayer != NULL)
1695         desktop->setCurrentLayer(newLayer);
1697     g_free(desktop->_reconstruction_old_layer_id);
1698     desktop->_reconstruction_old_layer_id = NULL;
1699     // printf("Desktop, finishing reconstruction end\n");
1700     return;
1703 /**
1704  * Namedview_modified callback.
1705  */
1706 static void
1707 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1709     SPNamedView *nv=SP_NAMEDVIEW(obj);
1711     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1713         /* Show/hide page background */
1714         if (nv->pagecolor & 0xff) {
1715             sp_canvas_item_show (desktop->table);
1716             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1717             sp_canvas_item_move_to_z (desktop->table, 0);
1718         } else {
1719             sp_canvas_item_hide (desktop->table);
1720         }
1722         /* Show/hide page border */
1723         if (nv->showborder) {
1724             // show
1725             sp_canvas_item_show (desktop->page_border);
1726             // set color and shadow
1727             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1728             if (nv->pageshadow) {
1729                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1730             }
1731             // place in the z-order stack
1732             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1733                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1734             } else {
1735                 int order = sp_canvas_item_order (desktop->page_border);
1736                 int morder = sp_canvas_item_order (desktop->drawing);
1737                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1738                     morder - order);
1739             }
1740         } else {
1741                 sp_canvas_item_hide (desktop->page_border);
1742                 if (nv->pageshadow) {
1743                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1744                 }
1745         }
1747         /* Show/hide page shadow */
1748         if (nv->showpageshadow && nv->pageshadow) {
1749             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1750         } else {
1751             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1752         }
1754         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1755         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1756             (SP_RGBA32_R_U(nv->pagecolor) +
1757              SP_RGBA32_G_U(nv->pagecolor) +
1758              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1759             // the background color is light or transparent, use black outline
1760             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1761         } else { // use white outline
1762             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1763         }
1764     }
1767 Geom::Matrix SPDesktop::w2d() const
1769     return _w2d;
1772 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1774     return p * _w2d;
1777 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1779     return p * _d2w;
1782 Geom::Matrix SPDesktop::doc2dt() const
1784     return _doc2dt;
1787 Geom::Matrix SPDesktop::dt2doc() const
1789     // doc2dt is its own inverse
1790     return _doc2dt;
1793 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1795     return p * _doc2dt;
1798 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1800     return p * dt2doc();
1804 /**
1805  * Pop event context from desktop's context stack. Never used.
1806  */
1807 // void
1808 // SPDesktop::pop_event_context (unsigned int key)
1809 // {
1810 //    SPEventContext *ec = NULL;
1811 //
1812 //    if (event_context && event_context->key == key) {
1813 //        g_return_if_fail (event_context);
1814 //        g_return_if_fail (event_context->next);
1815 //        ec = event_context;
1816 //        sp_event_context_deactivate (ec);
1817 //        event_context = ec->next;
1818 //        sp_event_context_activate (event_context);
1819 //        _event_context_changed_signal.emit (this, ec);
1820 //    }
1821 //
1822 //    SPEventContext *ref = event_context;
1823 //    while (ref && ref->next && ref->next->key != key)
1824 //        ref = ref->next;
1825 //
1826 //    if (ref && ref->next) {
1827 //        ec = ref->next;
1828 //        ref->next = ec->next;
1829 //    }
1830 //
1831 //    if (ec) {
1832 //        sp_event_context_finish (ec);
1833 //        g_object_unref (G_OBJECT (ec));
1834 //    }
1835 // }
1837 /*
1838   Local Variables:
1839   mode:c++
1840   c-file-style:"stroustrup"
1841   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1842   indent-tabs-mode:nil
1843   fill-column:99
1844   End:
1845 */
1846 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :