Code

fix crash: desktop->selection was not deleted when desktop is destroyed
[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 "prefs-utils.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);
107 static void _update_snap_distances (SPDesktop *desktop);
109 /**
110  * Return new desktop object.
111  * \pre namedview != NULL.
112  * \pre canvas != NULL.
113  */
114 SPDesktop::SPDesktop() :
115     _dlg_mgr( 0 ),
116     namedview( 0 ),
117     canvas( 0 ),
118     selection( 0 ),
119     event_context( 0 ),
120     layer_manager( 0 ),
121     event_log( 0 ),
122     temporary_item_list( 0 ),
123     snapindicator( 0 ),
124     acetate( 0 ),
125     main( 0 ),
126     gridgroup( 0 ),
127     guides( 0 ),
128     drawing( 0 ),
129     sketch( 0 ),
130     controls( 0 ),
131     tempgroup ( 0 ),
132     table( 0 ),
133     page( 0 ),
134     page_border( 0 ),
135     current( 0 ),
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( NR::Matrix(NR::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();
173     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
175     current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
177     namedview = nv;
178     canvas = aCanvas;
180     SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
181     /* Kill flicker */
182     sp_document_ensure_up_to_date (document);
184     /* Setup Dialog Manager */
185     _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
187     dkey = sp_item_display_key_new (1);
189     /* Connect document */
190     setDocument (document);
192     number = namedview->getViewCount();
195     /* Setup Canvas */
196     g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
198     SPCanvasGroup *root = sp_canvas_root (canvas);
200     /* Setup adminstrative layers */
201     acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
202     g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
203     main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
204     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
206     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
207     SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
208     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
209     sp_canvas_item_move_to_z (table, 0);
211     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
212     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
213     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
215     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
216     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
218     SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
220     if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
221         // Start in outline mode
222         setDisplayModeOutline();
223     } else {
224         // Start in normal mode, default
225         setDisplayModeNormal();
226     }
228     gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
229     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
230     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
231     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
232     tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
234     /* Push select tool to the bottom of stack */
235     /** \todo
236      * FIXME: this is the only call to this.  Everything else seems to just
237      * call "set" instead of "push".  Can we assume that there is only one
238      * context ever?
239      */
240     push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
242     // display rect and zoom are now handled in sp_desktop_widget_realize()
244     Geom::Rect const d(Geom::Point(0.0, 0.0),
245                        Geom::Point(sp_document_width(document), sp_document_height(document)));
247     SP_CTRLRECT(page)->setRectangle(d);
248     SP_CTRLRECT(page_border)->setRectangle(d);
250     /* the following sets the page shadow on the canvas
251        It was originally set to 5, which is really cheesy!
252        It now is an attribute in the document's namedview. If a value of
253        0 is used, then the constructor for a shadow is not initialized.
254     */
256     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
257         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
258     }
261     /* Connect event for page resize */
262     _doc2dt[5] = sp_document_height (document);
263     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
265     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
267     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
268             SP_CANVAS_ARENA (drawing)->arena,
269             dkey,
270             SP_ITEM_SHOW_DISPLAY);
271     if (ai) {
272         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
273     }
275     namedview->show(this);
276     /* Ugly hack */
277     activate_guides (true);
278     /* Ugly hack */
279     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
281 /* Set up notification of rebuilding the document, this allows
282        for saving object related settings in the document. */
283     _reconstruction_start_connection =
284         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
285     _reconstruction_finish_connection =
286         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
287     _reconstruction_old_layer_id = NULL;
289     // ?
290     // sp_active_desktop_set (desktop);
291     _inkscape = INKSCAPE;
293     _activate_connection = _activate_signal.connect(
294         sigc::bind(
295             sigc::ptr_fun(_onActivate),
296             this
297         )
298     );
299      _deactivate_connection = _deactivate_signal.connect(
300         sigc::bind(
301             sigc::ptr_fun(_onDeactivate),
302             this
303         )
304     );
306     _sel_modified_connection = selection->connectModified(
307         sigc::bind(
308             sigc::ptr_fun(&_onSelectionModified),
309             this
310         )
311     );
312     _sel_changed_connection = selection->connectChanged(
313         sigc::bind(
314             sigc::ptr_fun(&_onSelectionChanged),
315             this
316         )
317     );
320     /* setup LayerManager */
321     //   (Setting up after the connections are all in place, as it may use some of them)
322     layer_manager = new Inkscape::LayerManager( this );
324     showGrids(namedview->grids_visible, false);
326     temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
327     snapindicator = new Inkscape::Display::SnapIndicator ( this );
331 void SPDesktop::destroy()
333     if (snapindicator) {
334         delete snapindicator;
335         snapindicator = NULL;
336     }
337     if (temporary_item_list) {
338         delete temporary_item_list;
339         temporary_item_list = NULL;
340     }
342     if (selection) {
343         delete selection;
344         selection = NULL;
345     }
347     namedview->hide(this);
349     _activate_connection.disconnect();
350     _deactivate_connection.disconnect();
351     _sel_modified_connection.disconnect();
352     _sel_changed_connection.disconnect();
353     _modified_connection.disconnect();
354     _commit_connection.disconnect();
355     _reconstruction_start_connection.disconnect();
356     _reconstruction_finish_connection.disconnect();
358     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
359     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
360     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
362     while (event_context) {
363         SPEventContext *ec = event_context;
364         event_context = ec->next;
365         sp_event_context_finish (ec);
366         g_object_unref (G_OBJECT (ec));
367     }
369     if (_layer_hierarchy) {
370         delete _layer_hierarchy;
371 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
372     }
374     if (layer_manager) {
375         delete layer_manager;
376         layer_manager = NULL;
377     }
379     if (_inkscape) {
380         _inkscape = NULL;
381     }
383     if (drawing) {
384         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
385         drawing = NULL;
386     }
388     delete _guides_message_context;
389     _guides_message_context = NULL;
391     g_list_free (zooms_past);
392     g_list_free (zooms_future);
395 SPDesktop::~SPDesktop() {}
397 //--------------------------------------------------------------------
398 /* Public methods */
401 /* These methods help for temporarily showing things on-canvas.
402  * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
403  * is when you want to prematurely remove the item from the canvas, by calling
404  * desktop->remove_temporary_canvasitem(tempitem).
405  */
406 /** Note that lifetime is measured in milliseconds
407  * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
408  * delete the object for you and the reference will become invalid without you knowing it.
409  * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
410  * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
411  * because the object might be deleted already without you knowing it.
412  * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
413  */
414 Inkscape::Display::TemporaryItem *
415 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
417     if (move_to_bottom) {
418         sp_canvas_item_move_to_z(item, 0);
419     }
421     return temporary_item_list->add_item(item, lifetime);
424 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
425 */
426 void
427 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
429     // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
430     if (tempitem && temporary_item_list) {
431         temporary_item_list->delete_item(tempitem);
432     }
435 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
436     SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
437     canvas->rendermode = mode;
438     _display_mode = mode;
439     if (mode != Inkscape::RENDERMODE_OUTLINE) {
440         _saved_display_mode = _display_mode;
441     }
442     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
443     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
446 void SPDesktop::displayModeToggle() {
447     if (_display_mode == Inkscape::RENDERMODE_OUTLINE) {
448         _setDisplayMode(_saved_display_mode);
449     } else {
450         _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
451     }
454 /**
455  * Returns current root (=bottom) layer.
456  */
457 SPObject *SPDesktop::currentRoot() const
459     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
462 /**
463  * Returns current top layer.
464  */
465 SPObject *SPDesktop::currentLayer() const
467     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
470 /**
471  * Sets the current layer of the desktop.
472  *
473  * Make \a object the top layer.
474  */
475 void SPDesktop::setCurrentLayer(SPObject *object) {
476     g_return_if_fail(SP_IS_GROUP(object));
477     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
478     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
479     _layer_hierarchy->setBottom(object);
482 void SPDesktop::toggleLayerSolo(SPObject *object) {
483     g_return_if_fail(SP_IS_GROUP(object));
484     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
486     bool othersShowing = false;
487     std::vector<SPObject*> layers;
488     for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
489         layers.push_back(obj);
490         othersShowing |= !SP_ITEM(obj)->isHidden();
491     }
492     for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
493         layers.push_back(obj);
494         othersShowing |= !SP_ITEM(obj)->isHidden();
495     }
498     if ( SP_ITEM(object)->isHidden() ) {
499         SP_ITEM(object)->setHidden(false);
500     }
502     for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
503         SP_ITEM(*it)->setHidden(othersShowing);
504     }
507 /**
508  * Return layer that contains \a object.
509  */
510 SPObject *SPDesktop::layerForObject(SPObject *object) {
511     g_return_val_if_fail(object != NULL, NULL);
513     SPObject *root=currentRoot();
514     object = SP_OBJECT_PARENT(object);
515     while ( object && object != root && !isLayer(object) ) {
516         object = SP_OBJECT_PARENT(object);
517     }
518     return object;
521 /**
522  * True if object is a layer.
523  */
524 bool SPDesktop::isLayer(SPObject *object) const {
525     return ( SP_IS_GROUP(object)
526              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
527                   == SPGroup::LAYER ) );
530 /**
531  * True if desktop viewport fully contains \a item's bbox.
532  */
533 bool SPDesktop::isWithinViewport (SPItem *item) const
535     Geom::Rect const viewport = get_display_area();
536     boost::optional<NR::Rect> const bbox = sp_item_bbox_desktop(item);
537     if (bbox) {
538         return viewport.contains(to_2geom(*bbox));
539     } else {
540         return true;
541     }
544 ///
545 bool SPDesktop::itemIsHidden(SPItem const *item) const {
546     return item->isHidden(this->dkey);
549 /**
550  * Set activate property of desktop; emit signal if changed.
551  */
552 void
553 SPDesktop::set_active (bool new_active)
555     if (new_active != _active) {
556         _active = new_active;
557         if (new_active) {
558             _activate_signal.emit();
559         } else {
560             _deactivate_signal.emit();
561         }
562     }
565 /**
566  * Set activate status of current desktop's named view.
567  */
568 void
569 SPDesktop::activate_guides(bool activate)
571     guides_active = activate;
572     namedview->activateGuides(this, activate);
575 /**
576  * Make desktop switch documents.
577  */
578 void
579 SPDesktop::change_document (SPDocument *theDocument)
581     g_return_if_fail (theDocument != NULL);
583     /* unselect everything before switching documents */
584     selection->clear();
586     setDocument (theDocument);
588     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
589        (this can probably be done in a better way) */
590     Gtk::Window *parent = this->getToplevel();
591     g_assert(parent != NULL);
592     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
593     if (dtw) dtw->desktop = this;
594     sp_desktop_widget_update_namedview(dtw);
596     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
597     _document_replaced_signal.emit (this, theDocument);
600 /**
601  * Make desktop switch event contexts.
602  */
603 void
604 SPDesktop::set_event_context (GtkType type, const gchar *config)
606     SPEventContext *ec;
607     while (event_context) {
608         ec = event_context;
609         sp_event_context_deactivate (ec);
610         event_context = ec->next;
611         sp_event_context_finish (ec);
612         g_object_unref (G_OBJECT (ec));
613     }
615     Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
616     ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
617     ec->next = event_context;
618     event_context = ec;
619     sp_event_context_activate (ec);
620     _event_context_changed_signal.emit (this, ec);
623 /**
624  * Push event context onto desktop's context stack.
625  */
626 void
627 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
629     SPEventContext *ref, *ec;
630     Inkscape::XML::Node *repr;
632     if (event_context && event_context->key == key) return;
633     ref = event_context;
634     while (ref && ref->next && ref->next->key != key) ref = ref->next;
635     if (ref && ref->next) {
636         ec = ref->next;
637         ref->next = ec->next;
638         sp_event_context_finish (ec);
639         g_object_unref (G_OBJECT (ec));
640     }
642     if (event_context) sp_event_context_deactivate (event_context);
643     repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
644     ec = sp_event_context_new (type, this, repr, key);
645     ec->next = event_context;
646     event_context = ec;
647     sp_event_context_activate (ec);
648     _event_context_changed_signal.emit (this, ec);
651 /**
652  * Sets the coordinate status to a given point
653  */
654 void
655 SPDesktop::set_coordinate_status (Geom::Point p) {
656     _widget->setCoordinateStatus(p);
659 /**
660  * \see sp_document_item_from_list_at_point_bottom()
661  */
662 SPItem *
663 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
665     g_return_val_if_fail (doc() != NULL, NULL);
666     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
669 /**
670  * \see sp_document_item_at_point()
671  */
672 SPItem *
673 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
675     g_return_val_if_fail (doc() != NULL, NULL);
676     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
679 /**
680  * \see sp_document_group_at_point()
681  */
682 SPItem *
683 SPDesktop::group_at_point (Geom::Point const p) const
685     g_return_val_if_fail (doc() != NULL, NULL);
686     return sp_document_group_at_point (doc(), dkey, p);
689 /**
690  * \brief  Returns the mouse point in document coordinates; if mouse is
691  * outside the canvas, returns the center of canvas viewpoint
692  */
693 Geom::Point
694 SPDesktop::point() const
696     Geom::Point p = _widget->getPointer();
697     Geom::Point pw = sp_canvas_window_to_world (canvas, p);
698     p = w2d(pw);
700     Geom::Rect const r = canvas->getViewbox();
702     Geom::Point r0 = w2d(r.min());
703     Geom::Point r1 = w2d(r.max());
705     if (p[Geom::X] >= r0[Geom::X] &&
706         p[Geom::X] <= r1[Geom::X] &&
707         p[Geom::Y] >= r1[Geom::Y] &&
708         p[Geom::Y] <= r0[Geom::Y])
709     {
710         return p;
711     } else {
712         return (r0 + r1) / 2;
713     }
716 /**
717  * Put current zoom data in history list.
718  */
719 void
720 SPDesktop::push_current_zoom (GList **history)
722     Geom::Rect const area = get_display_area();
724     NRRect *old_zoom = g_new(NRRect, 1);
725     old_zoom->x0 = area.min()[NR::X];
726     old_zoom->x1 = area.max()[NR::X];
727     old_zoom->y0 = area.min()[NR::Y];
728     old_zoom->y1 = area.max()[NR::Y];
729     if ( *history == NULL
730          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
731                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
732                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
733                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
734     {
735         *history = g_list_prepend (*history, old_zoom);
736     }
739 /**
740  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
741  */
742 void
743 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
745     g_assert(_widget);
747     // save the zoom
748     if (log) {
749         push_current_zoom(&zooms_past);
750         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
751         g_list_free (zooms_future);
752         zooms_future = NULL;
753     }
755     double const cx = 0.5 * (x0 + x1);
756     double const cy = 0.5 * (y0 + y1);
758     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
759     Geom::Rect viewbox = canvas->getViewbox();
760     viewbox.expandBy(border);
762     double scale = _d2w.descrim();
763     double newscale;
764     if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
765         newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
766     } else {
767         newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
768     }
770     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
772     int clear = FALSE;
773     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
774         /* Set zoom factors */
775         _d2w = NR::Matrix(NR::scale(newscale, -newscale));
776         _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
777         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
778         clear = TRUE;
779     }
781     /* Calculate top left corner (in document pixels) */
782     x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
783     y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
785     /* Scroll */
786     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
788     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
789     sp_box3d_context_update_lines(event_context);
791     _widget->updateRulers();
792     _widget->updateScrollbars(_d2w.descrim());
793     _widget->updateZoom();
796 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
798     set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
801 /**
802  * Return viewbox dimensions.
803  */
804 Geom::Rect SPDesktop::get_display_area() const
806     Geom::Rect const viewbox = canvas->getViewbox();
808     double const scale = _d2w[0];
810     return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
811                       Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
814 /**
815  * Revert back to previous zoom if possible.
816  */
817 void
818 SPDesktop::prev_zoom()
820     if (zooms_past == NULL) {
821         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
822         return;
823     }
825     // push current zoom into forward zooms list
826     push_current_zoom (&zooms_future);
828     // restore previous zoom
829     set_display_area (((NRRect *) zooms_past->data)->x0,
830             ((NRRect *) zooms_past->data)->y0,
831             ((NRRect *) zooms_past->data)->x1,
832             ((NRRect *) zooms_past->data)->y1,
833             0, false);
835     // remove the just-added zoom from the past zooms list
836     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
839 /**
840  * Set zoom to next in list.
841  */
842 void
843 SPDesktop::next_zoom()
845     if (zooms_future == NULL) {
846         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
847         return;
848     }
850     // push current zoom into past zooms list
851     push_current_zoom (&zooms_past);
853     // restore next zoom
854     set_display_area (((NRRect *) zooms_future->data)->x0,
855             ((NRRect *) zooms_future->data)->y0,
856             ((NRRect *) zooms_future->data)->x1,
857             ((NRRect *) zooms_future->data)->y1,
858             0, false);
860     // remove the just-used zoom from the zooms_future list
861     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
864 /**
865  * Zoom to point with absolute zoom factor.
866  */
867 void
868 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
870     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
872     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
873     // this check prevents "sliding" when trying to zoom in at maximum zoom;
874     /// \todo someone please fix calculations properly and remove this hack
875     if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
876         return;
878     Geom::Rect const viewbox = canvas->getViewbox();
880     double const width2 = viewbox.dimensions()[NR::X] / zoom;
881     double const height2 = viewbox.dimensions()[NR::Y] / zoom;
883     set_display_area(cx - px * width2,
884                      cy - py * height2,
885                      cx + (1 - px) * width2,
886                      cy + (1 - py) * height2,
887                      0.0);
890 /**
891  * Zoom to center with absolute zoom factor.
892  */
893 void
894 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
896     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
899 /**
900  * Zoom to point with relative zoom factor.
901  */
902 void
903 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
905     Geom::Rect const area = get_display_area();
907     if (cx < area.min()[NR::X]) {
908         cx = area.min()[NR::X];
909     }
910     if (cx > area.max()[NR::X]) {
911         cx = area.max()[NR::X];
912     }
913     if (cy < area.min()[NR::Y]) {
914         cy = area.min()[NR::Y];
915     }
916     if (cy > area.max()[NR::Y]) {
917         cy = area.max()[NR::Y];
918     }
920     gdouble const scale = _d2w.descrim() * zoom;
921     double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
922     double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
924     zoom_absolute_keep_point(cx, cy, px, py, scale);
927 /**
928  * Zoom to center with relative zoom factor.
929  */
930 void
931 SPDesktop::zoom_relative (double cx, double cy, double zoom)
933     gdouble scale = _d2w.descrim() * zoom;
934     zoom_absolute (cx, cy, scale);
937 /**
938  * Set display area to origin and current document dimensions.
939  */
940 void
941 SPDesktop::zoom_page()
943     Geom::Rect d(Geom::Point(0, 0),
944                  Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
946     // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 1.0; is it safe to ignore it?
947     if (d.isEmpty()) {
948         return;
949     }
951     set_display_area(d, 10);
954 /**
955  * Set display area to current document width.
956  */
957 void
958 SPDesktop::zoom_page_width()
960     Geom::Rect const a = get_display_area();
962     if (sp_document_width(doc()) < 1.0) {
963         return;
964     }
966     Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
967                  Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
969     set_display_area(d, 10);
972 /**
973  * Zoom to selection.
974  */
975 void
976 SPDesktop::zoom_selection()
978     boost::optional<Geom::Rect> const d = to_2geom(selection->bounds());
980     // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 0.1; is it safe to ignore it?
981     if ( !d || d->isEmpty() ) {
982         return;
983     }
985     set_display_area(*d, 10);
988 /**
989  * Tell widget to let zoom widget grab keyboard focus.
990  */
991 void
992 SPDesktop::zoom_grab_focus()
994     _widget->letZoomGrabFocus();
997 /**
998  * Zoom to whole drawing.
999  */
1000 void
1001 SPDesktop::zoom_drawing()
1003     g_return_if_fail (doc() != NULL);
1004     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1005     g_return_if_fail (docitem != NULL);
1007     boost::optional<Geom::Rect> d = to_2geom(sp_item_bbox_desktop(docitem));
1009     /* Note that the second condition here indicates that
1010     ** there are no items in the drawing.
1011     */
1012     // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 1.0; is it safe to ignore it?
1013     if ( !d || d->isEmpty() ) {
1014         return;
1015     }
1017     set_display_area(*d, 10);
1020 /**
1021  * Scroll canvas by specific coordinate amount in svg coordinates.
1022  */
1023 void
1024 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1026     double scale = _d2w.descrim();
1027     scroll_world(dx*scale, dy*scale, is_scrolling);
1030 /**
1031  * Scroll canvas by specific coordinate amount.
1032  */
1033 void
1034 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1036     g_assert(_widget);
1038     Geom::Rect const viewbox = canvas->getViewbox();
1040     sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1042     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1043     sp_box3d_context_update_lines(event_context);
1045     _widget->updateRulers();
1046     _widget->updateScrollbars(_d2w.descrim());
1049 bool
1050 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1052     gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
1054     // autoscrolldistance is in screen pixels, but the display area is in document units
1055     autoscrolldistance /= _d2w.descrim();
1056     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1057     Geom::Rect dbox = get_display_area();
1058     dbox.expandBy(-autoscrolldistance);
1060     if (!(p[NR::X] > dbox.min()[NR::X] && p[NR::X] < dbox.max()[NR::X]) ||
1061         !(p[NR::Y] > dbox.min()[NR::Y] && p[NR::Y] < dbox.max()[NR::Y])   ) {
1063         Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1065         gdouble x_to;
1066         if (p[NR::X] < dbox.min()[NR::X])
1067             x_to = dbox.min()[NR::X];
1068         else if (p[NR::X] > dbox.max()[NR::X])
1069             x_to = dbox.max()[NR::X];
1070         else
1071             x_to = p[NR::X];
1073         gdouble y_to;
1074         if (p[NR::Y] < dbox.min()[NR::Y])
1075             y_to = dbox.min()[NR::Y];
1076         else if (p[NR::Y] > dbox.max()[NR::Y])
1077             y_to = dbox.max()[NR::Y];
1078         else
1079             y_to = p[NR::Y];
1081         Geom::Point const d_dt(x_to, y_to);
1082         Geom::Point const d_w( d_dt * _d2w );
1083         Geom::Point const moved_w( d_w - s_w );
1085         if (autoscrollspeed == 0)
1086             autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
1088         if (autoscrollspeed != 0)
1089             scroll_world (autoscrollspeed * moved_w);
1091         return true;
1092     }
1093     return false;
1096 bool
1097 SPDesktop::is_iconified()
1099     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1102 void
1103 SPDesktop::iconify()
1105     _widget->setIconified();
1108 bool
1109 SPDesktop::is_maximized()
1111     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1114 void
1115 SPDesktop::maximize()
1117     _widget->setMaximized();
1120 bool
1121 SPDesktop::is_fullscreen()
1123     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1126 void
1127 SPDesktop::fullscreen()
1129     _widget->setFullscreen();
1132 void
1133 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1135     _widget->getGeometry (x, y, w, h);
1138 void
1139 SPDesktop::setWindowPosition (Geom::Point p)
1141     _widget->setPosition (p);
1144 void
1145 SPDesktop::setWindowSize (gint w, gint h)
1147     _widget->setSize (w, h);
1150 void
1151 SPDesktop::setWindowTransient (void *p, int transient_policy)
1153     _widget->setTransient (p, transient_policy);
1156 Gtk::Window*
1157 SPDesktop::getToplevel( )
1159     return _widget->getWindow();
1162 void
1163 SPDesktop::presentWindow()
1165     _widget->present();
1168 bool
1169 SPDesktop::warnDialog (gchar *text)
1171     return _widget->warnDialog (text);
1174 void
1175 SPDesktop::toggleRulers()
1177     _widget->toggleRulers();
1180 void
1181 SPDesktop::toggleScrollbars()
1183     _widget->toggleScrollbars();
1186 void
1187 SPDesktop::layoutWidget()
1189     _widget->layout();
1192 void
1193 SPDesktop::destroyWidget()
1195     _widget->destroy();
1198 bool
1199 SPDesktop::shutdown()
1201     return _widget->shutdown();
1204 bool SPDesktop::onDeleteUI (GdkEventAny*)
1206     if(shutdown()) 
1207         return true;
1209     destroyWidget();
1210     return false;
1213 /**
1214  *  onWindowStateEvent
1215  *
1216  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1217  *  Since GTK doesn't have a way to query this state information directly, we
1218  *  record it for the desktop here, and also possibly trigger a layout.
1219  */
1220 bool
1221 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1223         // Record the desktop window's state
1224     window_state = event->new_window_state;
1226     // Layout may differ depending on full-screen mode or not
1227     GdkWindowState changed = event->changed_mask;
1228     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1229         layoutWidget();
1230     }
1232         return false;
1235 void
1236 SPDesktop::setToolboxFocusTo (gchar const *label)
1238     _widget->setToolboxFocusTo (label);
1241 void
1242 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1244     _widget->setToolboxAdjustmentValue (id, val);
1247 void
1248 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1250     _widget->setToolboxSelectOneValue (id, val);
1253 bool
1254 SPDesktop::isToolboxButtonActive (gchar const *id)
1256     return _widget->isToolboxButtonActive (id);
1259 void
1260 SPDesktop::emitToolSubselectionChanged(gpointer data)
1262         _tool_subselection_changed.emit(data);
1263         inkscape_subselection_changed (this);
1266 void
1267 SPDesktop::updateNow()
1269   sp_canvas_update_now(canvas);
1272 void
1273 SPDesktop::enableInteraction()
1275   _widget->enableInteraction();
1278 void SPDesktop::disableInteraction()
1280   _widget->disableInteraction();
1283 void SPDesktop::setWaitingCursor()
1285     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1286     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1287     gdk_cursor_unref(waiting);
1288     // GDK needs the flush for the cursor change to take effect
1289     gdk_flush();
1290     waiting_cursor = true;
1293 void SPDesktop::clearWaitingCursor()
1295   if (waiting_cursor)
1296       sp_event_context_update_cursor(sp_desktop_event_context(this));
1299 void SPDesktop::toggleColorProfAdjust()
1301     _widget->toggleColorProfAdjust();
1304 void SPDesktop::toggleGrids()
1306     if (namedview->grids) {
1307         if(gridgroup) {
1308             showGrids(!grids_visible);
1309         }
1310     } else {
1311         //there is no grid present at the moment. add a rectangular grid and make it visible
1312         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1313         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1314         showGrids(true);
1315     }
1318 void SPDesktop::showGrids(bool show, bool dirty_document)
1320     grids_visible = show;
1321     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1322     if (show) {
1323         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1324     } else {
1325         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1326     }
1329 void SPDesktop::toggleSnapping()
1331     bool v = namedview->snap_manager.getSnapEnabledGlobally();
1332     Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1333     sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1336 //----------------------------------------------------------------------
1337 // Callback implementations. The virtual ones are connected by the view.
1339 void
1340 SPDesktop::onPositionSet (double x, double y)
1342     _widget->viewSetPosition (Geom::Point(x,y));
1345 void
1346 SPDesktop::onResized (double /*x*/, double /*y*/)
1348    // Nothing called here
1351 /**
1352  * Redraw callback; queues Gtk redraw; connected by View.
1353  */
1354 void
1355 SPDesktop::onRedrawRequested ()
1357     if (main) {
1358         _widget->requestCanvasUpdate();
1359     }
1362 void
1363 SPDesktop::updateCanvasNow()
1365   _widget->requestCanvasUpdateAndWait();
1368 /**
1369  * Associate document with desktop.
1370  */
1371 void
1372 SPDesktop::setDocument (SPDocument *doc)
1374     if (this->doc() && doc) {
1375         namedview->hide(this);
1376         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1377     }
1379     if (_layer_hierarchy) {
1380         _layer_hierarchy->clear();
1381         delete _layer_hierarchy;
1382     }
1383     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1384     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1385     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1386     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1387     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1389     /* setup EventLog */
1390     event_log = new Inkscape::EventLog(doc);
1391     doc->addUndoObserver(*event_log);
1393     _commit_connection.disconnect();
1394     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1396     /// \todo fixme: This condition exists to make sure the code
1397     /// inside is NOT called on initialization, only on replacement. But there
1398     /// are surely more safe methods to accomplish this.
1399     // TODO since the comment had reversed logic, check the intent of this block of code:
1400     if (drawing) {
1401         NRArenaItem *ai = 0;
1403         namedview = sp_document_namedview (doc, NULL);
1404         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1405         number = namedview->getViewCount();
1407         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1408                 SP_CANVAS_ARENA (drawing)->arena,
1409                 dkey,
1410                 SP_ITEM_SHOW_DISPLAY);
1411         if (ai) {
1412             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1413         }
1414         namedview->show(this);
1415         /* Ugly hack */
1416         activate_guides (true);
1417         /* Ugly hack */
1418         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1419     }
1421     _document_replaced_signal.emit (this, doc);
1423     View::setDocument (doc);
1426 void
1427 SPDesktop::onStatusMessage
1428 (Inkscape::MessageType type, gchar const *message)
1430     if (_widget) {
1431         _widget->setMessage(type, message);
1432     }
1435 void
1436 SPDesktop::onDocumentURISet (gchar const* uri)
1438     _widget->setTitle(uri);
1441 /**
1442  * Resized callback.
1443  */
1444 void
1445 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1447     _doc2dt[5] = height;
1448     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1449     Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1450     SP_CTRLRECT(page)->setRectangle(a);
1451     SP_CTRLRECT(page_border)->setRectangle(a);
1455 void
1456 SPDesktop::_onActivate (SPDesktop* dt)
1458     if (!dt->_widget) return;
1459     dt->_widget->activateDesktop();
1462 void
1463 SPDesktop::_onDeactivate (SPDesktop* dt)
1465     if (!dt->_widget) return;
1466     dt->_widget->deactivateDesktop();
1469 void
1470 SPDesktop::_onSelectionModified
1471 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1473     if (!dt->_widget) return;
1474     dt->_widget->updateScrollbars (dt->_d2w.descrim());
1477 static void
1478 _onSelectionChanged
1479 (Inkscape::Selection *selection, SPDesktop *desktop)
1481     /** \todo
1482      * only change the layer for single selections, or what?
1483      * This seems reasonable -- for multiple selections there can be many
1484      * different layers involved.
1485      */
1486     SPItem *item=selection->singleItem();
1487     if (item) {
1488         SPObject *layer=desktop->layerForObject(item);
1489         if ( layer && layer != desktop->currentLayer() ) {
1490             desktop->setCurrentLayer(layer);
1491         }
1492     }
1495 /**
1496  * Calls event handler of current event context.
1497  * \param arena Unused
1498  * \todo fixme
1499  */
1500 static gint
1501 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1503     if (ai) {
1504         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1505         return sp_event_context_item_handler (desktop->event_context, spi, event);
1506     } else {
1507         return sp_event_context_root_handler (desktop->event_context, event);
1508     }
1511 static void
1512 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1513     g_return_if_fail(SP_IS_GROUP(layer));
1514     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1517 /// Callback
1518 static void
1519 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1520     g_return_if_fail(SP_IS_GROUP(layer));
1521     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1524 /// Callback
1525 static void
1526 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1527                                          SPDesktop *desktop)
1529     desktop->_layer_changed_signal.emit (bottom);
1532 /// Called when document is starting to be rebuilt.
1533 static void
1534 _reconstruction_start (SPDesktop * desktop)
1536     // printf("Desktop, starting reconstruction\n");
1537     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1538     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1540     /*
1541     GSList const * selection_objs = desktop->selection->list();
1542     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1544     }
1545     */
1546     desktop->selection->clear();
1548     // printf("Desktop, starting reconstruction end\n");
1551 /// Called when document rebuild is finished.
1552 static void
1553 _reconstruction_finish (SPDesktop * desktop)
1555     // printf("Desktop, finishing reconstruction\n");
1556     if (desktop->_reconstruction_old_layer_id == NULL)
1557         return;
1559     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1560     if (newLayer != NULL)
1561         desktop->setCurrentLayer(newLayer);
1563     g_free(desktop->_reconstruction_old_layer_id);
1564     desktop->_reconstruction_old_layer_id = NULL;
1565     // printf("Desktop, finishing reconstruction end\n");
1566     return;
1569 /**
1570  * Namedview_modified callback.
1571  */
1572 static void
1573 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1575     SPNamedView *nv=SP_NAMEDVIEW(obj);
1577     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1579         /* Recalculate snap distances */
1580         /* FIXME: why is the desktop getting involved in setting up something
1581         ** that is entirely to do with the namedview?
1582         */
1583         _update_snap_distances (desktop);
1585         /* Show/hide page background */
1586         if (nv->pagecolor & 0xff) {
1587             sp_canvas_item_show (desktop->table);
1588             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1589             sp_canvas_item_move_to_z (desktop->table, 0);
1590         } else {
1591             sp_canvas_item_hide (desktop->table);
1592         }
1594         /* Show/hide page border */
1595         if (nv->showborder) {
1596             // show
1597             sp_canvas_item_show (desktop->page_border);
1598             // set color and shadow
1599             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1600             if (nv->pageshadow) {
1601                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1602             }
1603             // place in the z-order stack
1604             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1605                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1606             } else {
1607                 int order = sp_canvas_item_order (desktop->page_border);
1608                 int morder = sp_canvas_item_order (desktop->drawing);
1609                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1610                                     morder - order);
1611             }
1612         } else {
1613                 sp_canvas_item_hide (desktop->page_border);
1614                 if (nv->pageshadow) {
1615                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1616                 }
1617         }
1619         /* Show/hide page shadow */
1620         if (nv->showpageshadow && nv->pageshadow) {
1621             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1622         } else {
1623             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1624         }
1626         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1627             (SP_RGBA32_R_U(nv->pagecolor) +
1628              SP_RGBA32_G_U(nv->pagecolor) +
1629              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1630             // the background color is light or transparent, use black outline
1631             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1632         } else { // use white outline
1633             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1634         }
1635     }
1638 /**
1639  * Callback to reset snapper's distances.
1640  */
1641 static void
1642 _update_snap_distances (SPDesktop *desktop)
1644     SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1646     SPNamedView &nv = *desktop->namedview;
1648     //tell all grid snappers
1649     for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1650         Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1651         grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
1652                                                                       *nv.gridtoleranceunit,
1653                                                                       px));
1654     }
1656     nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
1657                                                                        *nv.guidetoleranceunit,
1658                                                                        px));
1659     nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
1660                                                                         *nv.objecttoleranceunit,
1661                                                                         px));
1665 Geom::Matrix SPDesktop::w2d() const
1667     return _w2d;
1670 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1672     return p * _w2d;
1675 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1677     return p * _d2w;
1680 Geom::Matrix SPDesktop::doc2dt() const
1682     return _doc2dt;
1685 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1687     return p * _doc2dt;
1690 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1692     return p * _doc2dt.inverse();
1696 /**
1697  * Pop event context from desktop's context stack. Never used.
1698  */
1699 // void
1700 // SPDesktop::pop_event_context (unsigned int key)
1701 // {
1702 //    SPEventContext *ec = NULL;
1703 //
1704 //    if (event_context && event_context->key == key) {
1705 //        g_return_if_fail (event_context);
1706 //        g_return_if_fail (event_context->next);
1707 //        ec = event_context;
1708 //        sp_event_context_deactivate (ec);
1709 //        event_context = ec->next;
1710 //        sp_event_context_activate (event_context);
1711 //        _event_context_changed_signal.emit (this, ec);
1712 //    }
1713 //
1714 //    SPEventContext *ref = event_context;
1715 //    while (ref && ref->next && ref->next->key != key)
1716 //        ref = ref->next;
1717 //
1718 //    if (ref && ref->next) {
1719 //        ec = ref->next;
1720 //        ref->next = ec->next;
1721 //    }
1722 //
1723 //    if (ec) {
1724 //        sp_event_context_finish (ec);
1725 //        g_object_unref (G_OBJECT (ec));
1726 //    }
1727 // }
1729 /*
1730   Local Variables:
1731   mode:c++
1732   c-file-style:"stroustrup"
1733   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1734   indent-tabs-mode:nil
1735   fill-column:99
1736   End:
1737 */
1738 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :