Code

Next roud of NR ==> Geom conversion
[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     namedview->hide(this);
344     _activate_connection.disconnect();
345     _deactivate_connection.disconnect();
346     _sel_modified_connection.disconnect();
347     _sel_changed_connection.disconnect();
348     _modified_connection.disconnect();
349     _commit_connection.disconnect();
350     _reconstruction_start_connection.disconnect();
351     _reconstruction_finish_connection.disconnect();
353     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
354     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
355     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
357     while (event_context) {
358         SPEventContext *ec = event_context;
359         event_context = ec->next;
360         sp_event_context_finish (ec);
361         g_object_unref (G_OBJECT (ec));
362     }
364     if (_layer_hierarchy) {
365         delete _layer_hierarchy;
366 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
367     }
369     if (layer_manager) {
370         delete layer_manager;
371         layer_manager = NULL;
372     }
374     if (_inkscape) {
375         _inkscape = NULL;
376     }
378     if (drawing) {
379         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
380         drawing = NULL;
381     }
383     delete _guides_message_context;
384     _guides_message_context = NULL;
386     g_list_free (zooms_past);
387     g_list_free (zooms_future);
390 SPDesktop::~SPDesktop() {}
392 //--------------------------------------------------------------------
393 /* Public methods */
396 /* These methods help for temporarily showing things on-canvas.
397  * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
398  * is when you want to prematurely remove the item from the canvas, by calling
399  * desktop->remove_temporary_canvasitem(tempitem).
400  */
401 /** Note that lifetime is measured in milliseconds
402  * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
403  * delete the object for you and the reference will become invalid without you knowing it.
404  * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
405  * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
406  * because the object might be deleted already without you knowing it.
407  * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
408  */
409 Inkscape::Display::TemporaryItem *
410 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
412     if (move_to_bottom) {
413         sp_canvas_item_move_to_z(item, 0);
414     }
416     return temporary_item_list->add_item(item, lifetime);
419 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
420 */
421 void
422 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
424     // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
425     if (tempitem && temporary_item_list) {
426         temporary_item_list->delete_item(tempitem);
427     }
430 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
431     SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
432     canvas->rendermode = mode;
433     _display_mode = mode;
434     if (mode != Inkscape::RENDERMODE_OUTLINE) {
435         _saved_display_mode = _display_mode;
436     }
437     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
438     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
441 void SPDesktop::displayModeToggle() {
442     if (_display_mode == Inkscape::RENDERMODE_OUTLINE) {
443         _setDisplayMode(_saved_display_mode);
444     } else {
445         _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
446     }
449 /**
450  * Returns current root (=bottom) layer.
451  */
452 SPObject *SPDesktop::currentRoot() const
454     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
457 /**
458  * Returns current top layer.
459  */
460 SPObject *SPDesktop::currentLayer() const
462     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
465 /**
466  * Sets the current layer of the desktop.
467  *
468  * Make \a object the top layer.
469  */
470 void SPDesktop::setCurrentLayer(SPObject *object) {
471     g_return_if_fail(SP_IS_GROUP(object));
472     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
473     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
474     _layer_hierarchy->setBottom(object);
477 void SPDesktop::toggleLayerSolo(SPObject *object) {
478     g_return_if_fail(SP_IS_GROUP(object));
479     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
481     bool othersShowing = false;
482     std::vector<SPObject*> layers;
483     for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
484         layers.push_back(obj);
485         othersShowing |= !SP_ITEM(obj)->isHidden();
486     }
487     for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
488         layers.push_back(obj);
489         othersShowing |= !SP_ITEM(obj)->isHidden();
490     }
493     if ( SP_ITEM(object)->isHidden() ) {
494         SP_ITEM(object)->setHidden(false);
495     }
497     for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
498         SP_ITEM(*it)->setHidden(othersShowing);
499     }
502 /**
503  * Return layer that contains \a object.
504  */
505 SPObject *SPDesktop::layerForObject(SPObject *object) {
506     g_return_val_if_fail(object != NULL, NULL);
508     SPObject *root=currentRoot();
509     object = SP_OBJECT_PARENT(object);
510     while ( object && object != root && !isLayer(object) ) {
511         object = SP_OBJECT_PARENT(object);
512     }
513     return object;
516 /**
517  * True if object is a layer.
518  */
519 bool SPDesktop::isLayer(SPObject *object) const {
520     return ( SP_IS_GROUP(object)
521              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
522                   == SPGroup::LAYER ) );
525 /**
526  * True if desktop viewport fully contains \a item's bbox.
527  */
528 bool SPDesktop::isWithinViewport (SPItem *item) const
530     Geom::Rect const viewport = get_display_area();
531     boost::optional<NR::Rect> const bbox = sp_item_bbox_desktop(item);
532     if (bbox) {
533         return viewport.contains(to_2geom(*bbox));
534     } else {
535         return true;
536     }
539 ///
540 bool SPDesktop::itemIsHidden(SPItem const *item) const {
541     return item->isHidden(this->dkey);
544 /**
545  * Set activate property of desktop; emit signal if changed.
546  */
547 void
548 SPDesktop::set_active (bool new_active)
550     if (new_active != _active) {
551         _active = new_active;
552         if (new_active) {
553             _activate_signal.emit();
554         } else {
555             _deactivate_signal.emit();
556         }
557     }
560 /**
561  * Set activate status of current desktop's named view.
562  */
563 void
564 SPDesktop::activate_guides(bool activate)
566     guides_active = activate;
567     namedview->activateGuides(this, activate);
570 /**
571  * Make desktop switch documents.
572  */
573 void
574 SPDesktop::change_document (SPDocument *theDocument)
576     g_return_if_fail (theDocument != NULL);
578     /* unselect everything before switching documents */
579     selection->clear();
581     setDocument (theDocument);
583     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
584        (this can probably be done in a better way) */
585     Gtk::Window *parent = this->getToplevel();
586     g_assert(parent != NULL);
587     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
588     if (dtw) dtw->desktop = this;
589     sp_desktop_widget_update_namedview(dtw);
591     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
592     _document_replaced_signal.emit (this, theDocument);
595 /**
596  * Make desktop switch event contexts.
597  */
598 void
599 SPDesktop::set_event_context (GtkType type, const gchar *config)
601     SPEventContext *ec;
602     while (event_context) {
603         ec = event_context;
604         sp_event_context_deactivate (ec);
605         event_context = ec->next;
606         sp_event_context_finish (ec);
607         g_object_unref (G_OBJECT (ec));
608     }
610     Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
611     ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
612     ec->next = event_context;
613     event_context = ec;
614     sp_event_context_activate (ec);
615     _event_context_changed_signal.emit (this, ec);
618 /**
619  * Push event context onto desktop's context stack.
620  */
621 void
622 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
624     SPEventContext *ref, *ec;
625     Inkscape::XML::Node *repr;
627     if (event_context && event_context->key == key) return;
628     ref = event_context;
629     while (ref && ref->next && ref->next->key != key) ref = ref->next;
630     if (ref && ref->next) {
631         ec = ref->next;
632         ref->next = ec->next;
633         sp_event_context_finish (ec);
634         g_object_unref (G_OBJECT (ec));
635     }
637     if (event_context) sp_event_context_deactivate (event_context);
638     repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
639     ec = sp_event_context_new (type, this, repr, key);
640     ec->next = event_context;
641     event_context = ec;
642     sp_event_context_activate (ec);
643     _event_context_changed_signal.emit (this, ec);
646 /**
647  * Sets the coordinate status to a given point
648  */
649 void
650 SPDesktop::set_coordinate_status (Geom::Point p) {
651     _widget->setCoordinateStatus(p);
654 /**
655  * \see sp_document_item_from_list_at_point_bottom()
656  */
657 SPItem *
658 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
660     g_return_val_if_fail (doc() != NULL, NULL);
661     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
664 /**
665  * \see sp_document_item_at_point()
666  */
667 SPItem *
668 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
670     g_return_val_if_fail (doc() != NULL, NULL);
671     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
674 /**
675  * \see sp_document_group_at_point()
676  */
677 SPItem *
678 SPDesktop::group_at_point (Geom::Point const p) const
680     g_return_val_if_fail (doc() != NULL, NULL);
681     return sp_document_group_at_point (doc(), dkey, p);
684 /**
685  * \brief  Returns the mouse point in document coordinates; if mouse is
686  * outside the canvas, returns the center of canvas viewpoint
687  */
688 Geom::Point
689 SPDesktop::point() const
691     Geom::Point p = _widget->getPointer();
692     Geom::Point pw = sp_canvas_window_to_world (canvas, p);
693     p = w2d(pw);
695     Geom::Rect const r = canvas->getViewbox();
697     Geom::Point r0 = w2d(r.min());
698     Geom::Point r1 = w2d(r.max());
700     if (p[Geom::X] >= r0[Geom::X] &&
701         p[Geom::X] <= r1[Geom::X] &&
702         p[Geom::Y] >= r1[Geom::Y] &&
703         p[Geom::Y] <= r0[Geom::Y])
704     {
705         return p;
706     } else {
707         return (r0 + r1) / 2;
708     }
711 /**
712  * Put current zoom data in history list.
713  */
714 void
715 SPDesktop::push_current_zoom (GList **history)
717     Geom::Rect const area = get_display_area();
719     NRRect *old_zoom = g_new(NRRect, 1);
720     old_zoom->x0 = area.min()[NR::X];
721     old_zoom->x1 = area.max()[NR::X];
722     old_zoom->y0 = area.min()[NR::Y];
723     old_zoom->y1 = area.max()[NR::Y];
724     if ( *history == NULL
725          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
726                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
727                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
728                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
729     {
730         *history = g_list_prepend (*history, old_zoom);
731     }
734 /**
735  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
736  */
737 void
738 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
740     g_assert(_widget);
742     // save the zoom
743     if (log) {
744         push_current_zoom(&zooms_past);
745         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
746         g_list_free (zooms_future);
747         zooms_future = NULL;
748     }
750     double const cx = 0.5 * (x0 + x1);
751     double const cy = 0.5 * (y0 + y1);
753     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
754     Geom::Rect viewbox = canvas->getViewbox();
755     viewbox.expandBy(border);
757     double scale = _d2w.descrim();
758     double newscale;
759     if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
760         newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
761     } else {
762         newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
763     }
765     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
767     int clear = FALSE;
768     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
769         /* Set zoom factors */
770         _d2w = NR::Matrix(NR::scale(newscale, -newscale));
771         _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
772         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
773         clear = TRUE;
774     }
776     /* Calculate top left corner (in document pixels) */
777     x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
778     y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
780     /* Scroll */
781     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
783     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
784     sp_box3d_context_update_lines(event_context);
786     _widget->updateRulers();
787     _widget->updateScrollbars(_d2w.descrim());
788     _widget->updateZoom();
791 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
793     set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
796 /**
797  * Return viewbox dimensions.
798  */
799 Geom::Rect SPDesktop::get_display_area() const
801     Geom::Rect const viewbox = canvas->getViewbox();
803     double const scale = _d2w[0];
805     return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
806                       Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
809 /**
810  * Revert back to previous zoom if possible.
811  */
812 void
813 SPDesktop::prev_zoom()
815     if (zooms_past == NULL) {
816         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
817         return;
818     }
820     // push current zoom into forward zooms list
821     push_current_zoom (&zooms_future);
823     // restore previous zoom
824     set_display_area (((NRRect *) zooms_past->data)->x0,
825             ((NRRect *) zooms_past->data)->y0,
826             ((NRRect *) zooms_past->data)->x1,
827             ((NRRect *) zooms_past->data)->y1,
828             0, false);
830     // remove the just-added zoom from the past zooms list
831     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
834 /**
835  * Set zoom to next in list.
836  */
837 void
838 SPDesktop::next_zoom()
840     if (zooms_future == NULL) {
841         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
842         return;
843     }
845     // push current zoom into past zooms list
846     push_current_zoom (&zooms_past);
848     // restore next zoom
849     set_display_area (((NRRect *) zooms_future->data)->x0,
850             ((NRRect *) zooms_future->data)->y0,
851             ((NRRect *) zooms_future->data)->x1,
852             ((NRRect *) zooms_future->data)->y1,
853             0, false);
855     // remove the just-used zoom from the zooms_future list
856     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
859 /**
860  * Zoom to point with absolute zoom factor.
861  */
862 void
863 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
865     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
867     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
868     // this check prevents "sliding" when trying to zoom in at maximum zoom;
869     /// \todo someone please fix calculations properly and remove this hack
870     if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
871         return;
873     Geom::Rect const viewbox = canvas->getViewbox();
875     double const width2 = viewbox.dimensions()[NR::X] / zoom;
876     double const height2 = viewbox.dimensions()[NR::Y] / zoom;
878     set_display_area(cx - px * width2,
879                      cy - py * height2,
880                      cx + (1 - px) * width2,
881                      cy + (1 - py) * height2,
882                      0.0);
885 /**
886  * Zoom to center with absolute zoom factor.
887  */
888 void
889 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
891     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
894 /**
895  * Zoom to point with relative zoom factor.
896  */
897 void
898 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
900     Geom::Rect const area = get_display_area();
902     if (cx < area.min()[NR::X]) {
903         cx = area.min()[NR::X];
904     }
905     if (cx > area.max()[NR::X]) {
906         cx = area.max()[NR::X];
907     }
908     if (cy < area.min()[NR::Y]) {
909         cy = area.min()[NR::Y];
910     }
911     if (cy > area.max()[NR::Y]) {
912         cy = area.max()[NR::Y];
913     }
915     gdouble const scale = _d2w.descrim() * zoom;
916     double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
917     double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
919     zoom_absolute_keep_point(cx, cy, px, py, scale);
922 /**
923  * Zoom to center with relative zoom factor.
924  */
925 void
926 SPDesktop::zoom_relative (double cx, double cy, double zoom)
928     gdouble scale = _d2w.descrim() * zoom;
929     zoom_absolute (cx, cy, scale);
932 /**
933  * Set display area to origin and current document dimensions.
934  */
935 void
936 SPDesktop::zoom_page()
938     Geom::Rect d(Geom::Point(0, 0),
939                  Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
941     // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 1.0; is it safe to ignore it?
942     if (d.isEmpty()) {
943         return;
944     }
946     set_display_area(d, 10);
949 /**
950  * Set display area to current document width.
951  */
952 void
953 SPDesktop::zoom_page_width()
955     Geom::Rect const a = get_display_area();
957     if (sp_document_width(doc()) < 1.0) {
958         return;
959     }
961     Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
962                  Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
964     set_display_area(d, 10);
967 /**
968  * Zoom to selection.
969  */
970 void
971 SPDesktop::zoom_selection()
973     boost::optional<Geom::Rect> const d = to_2geom(selection->bounds());
975     // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 0.1; is it safe to ignore it?
976     if ( !d || d->isEmpty() ) {
977         return;
978     }
980     set_display_area(*d, 10);
983 /**
984  * Tell widget to let zoom widget grab keyboard focus.
985  */
986 void
987 SPDesktop::zoom_grab_focus()
989     _widget->letZoomGrabFocus();
992 /**
993  * Zoom to whole drawing.
994  */
995 void
996 SPDesktop::zoom_drawing()
998     g_return_if_fail (doc() != NULL);
999     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1000     g_return_if_fail (docitem != NULL);
1002     boost::optional<Geom::Rect> d = to_2geom(sp_item_bbox_desktop(docitem));
1004     /* Note that the second condition here indicates that
1005     ** there are no items in the drawing.
1006     */
1007     // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 1.0; is it safe to ignore it?
1008     if ( !d || d->isEmpty() ) {
1009         return;
1010     }
1012     set_display_area(*d, 10);
1015 /**
1016  * Scroll canvas by specific coordinate amount in svg coordinates.
1017  */
1018 void
1019 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1021     double scale = _d2w.descrim();
1022     scroll_world(dx*scale, dy*scale, is_scrolling);
1025 /**
1026  * Scroll canvas by specific coordinate amount.
1027  */
1028 void
1029 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1031     g_assert(_widget);
1033     Geom::Rect const viewbox = canvas->getViewbox();
1035     sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1037     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1038     sp_box3d_context_update_lines(event_context);
1040     _widget->updateRulers();
1041     _widget->updateScrollbars(_d2w.descrim());
1044 bool
1045 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1047     gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
1049     // autoscrolldistance is in screen pixels, but the display area is in document units
1050     autoscrolldistance /= _d2w.descrim();
1051     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1052     Geom::Rect dbox = get_display_area();
1053     dbox.expandBy(-autoscrolldistance);
1055     if (!(p[NR::X] > dbox.min()[NR::X] && p[NR::X] < dbox.max()[NR::X]) ||
1056         !(p[NR::Y] > dbox.min()[NR::Y] && p[NR::Y] < dbox.max()[NR::Y])   ) {
1058         Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1060         gdouble x_to;
1061         if (p[NR::X] < dbox.min()[NR::X])
1062             x_to = dbox.min()[NR::X];
1063         else if (p[NR::X] > dbox.max()[NR::X])
1064             x_to = dbox.max()[NR::X];
1065         else
1066             x_to = p[NR::X];
1068         gdouble y_to;
1069         if (p[NR::Y] < dbox.min()[NR::Y])
1070             y_to = dbox.min()[NR::Y];
1071         else if (p[NR::Y] > dbox.max()[NR::Y])
1072             y_to = dbox.max()[NR::Y];
1073         else
1074             y_to = p[NR::Y];
1076         Geom::Point const d_dt(x_to, y_to);
1077         Geom::Point const d_w( d_dt * _d2w );
1078         Geom::Point const moved_w( d_w - s_w );
1080         if (autoscrollspeed == 0)
1081             autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
1083         if (autoscrollspeed != 0)
1084             scroll_world (autoscrollspeed * moved_w);
1086         return true;
1087     }
1088     return false;
1091 bool
1092 SPDesktop::is_iconified()
1094     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1097 void
1098 SPDesktop::iconify()
1100     _widget->setIconified();
1103 bool
1104 SPDesktop::is_maximized()
1106     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1109 void
1110 SPDesktop::maximize()
1112     _widget->setMaximized();
1115 bool
1116 SPDesktop::is_fullscreen()
1118     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1121 void
1122 SPDesktop::fullscreen()
1124     _widget->setFullscreen();
1127 void
1128 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1130     _widget->getGeometry (x, y, w, h);
1133 void
1134 SPDesktop::setWindowPosition (Geom::Point p)
1136     _widget->setPosition (p);
1139 void
1140 SPDesktop::setWindowSize (gint w, gint h)
1142     _widget->setSize (w, h);
1145 void
1146 SPDesktop::setWindowTransient (void *p, int transient_policy)
1148     _widget->setTransient (p, transient_policy);
1151 Gtk::Window*
1152 SPDesktop::getToplevel( )
1154     return _widget->getWindow();
1157 void
1158 SPDesktop::presentWindow()
1160     _widget->present();
1163 bool
1164 SPDesktop::warnDialog (gchar *text)
1166     return _widget->warnDialog (text);
1169 void
1170 SPDesktop::toggleRulers()
1172     _widget->toggleRulers();
1175 void
1176 SPDesktop::toggleScrollbars()
1178     _widget->toggleScrollbars();
1181 void
1182 SPDesktop::layoutWidget()
1184     _widget->layout();
1187 void
1188 SPDesktop::destroyWidget()
1190     _widget->destroy();
1193 bool
1194 SPDesktop::shutdown()
1196     return _widget->shutdown();
1199 bool SPDesktop::onDeleteUI (GdkEventAny*)
1201     if(shutdown()) 
1202         return true;
1204     destroyWidget();
1205     return false;
1208 /**
1209  *  onWindowStateEvent
1210  *
1211  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1212  *  Since GTK doesn't have a way to query this state information directly, we
1213  *  record it for the desktop here, and also possibly trigger a layout.
1214  */
1215 bool
1216 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1218         // Record the desktop window's state
1219     window_state = event->new_window_state;
1221     // Layout may differ depending on full-screen mode or not
1222     GdkWindowState changed = event->changed_mask;
1223     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1224         layoutWidget();
1225     }
1227         return false;
1230 void
1231 SPDesktop::setToolboxFocusTo (gchar const *label)
1233     _widget->setToolboxFocusTo (label);
1236 void
1237 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1239     _widget->setToolboxAdjustmentValue (id, val);
1242 void
1243 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1245     _widget->setToolboxSelectOneValue (id, val);
1248 bool
1249 SPDesktop::isToolboxButtonActive (gchar const *id)
1251     return _widget->isToolboxButtonActive (id);
1254 void
1255 SPDesktop::emitToolSubselectionChanged(gpointer data)
1257         _tool_subselection_changed.emit(data);
1258         inkscape_subselection_changed (this);
1261 void
1262 SPDesktop::updateNow()
1264   sp_canvas_update_now(canvas);
1267 void
1268 SPDesktop::enableInteraction()
1270   _widget->enableInteraction();
1273 void SPDesktop::disableInteraction()
1275   _widget->disableInteraction();
1278 void SPDesktop::setWaitingCursor()
1280     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1281     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1282     gdk_cursor_unref(waiting);
1283     // GDK needs the flush for the cursor change to take effect
1284     gdk_flush();
1285     waiting_cursor = true;
1288 void SPDesktop::clearWaitingCursor()
1290   if (waiting_cursor)
1291       sp_event_context_update_cursor(sp_desktop_event_context(this));
1294 void SPDesktop::toggleColorProfAdjust()
1296     _widget->toggleColorProfAdjust();
1299 void SPDesktop::toggleGrids()
1301     if (namedview->grids) {
1302         if(gridgroup) {
1303             showGrids(!grids_visible);
1304         }
1305     } else {
1306         //there is no grid present at the moment. add a rectangular grid and make it visible
1307         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1308         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1309         showGrids(true);
1310     }
1313 void SPDesktop::showGrids(bool show, bool dirty_document)
1315     grids_visible = show;
1316     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1317     if (show) {
1318         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1319     } else {
1320         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1321     }
1324 void SPDesktop::toggleSnapping()
1326     bool v = namedview->snap_manager.getSnapEnabledGlobally();
1327     Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1328     sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1331 //----------------------------------------------------------------------
1332 // Callback implementations. The virtual ones are connected by the view.
1334 void
1335 SPDesktop::onPositionSet (double x, double y)
1337     _widget->viewSetPosition (Geom::Point(x,y));
1340 void
1341 SPDesktop::onResized (double /*x*/, double /*y*/)
1343    // Nothing called here
1346 /**
1347  * Redraw callback; queues Gtk redraw; connected by View.
1348  */
1349 void
1350 SPDesktop::onRedrawRequested ()
1352     if (main) {
1353         _widget->requestCanvasUpdate();
1354     }
1357 void
1358 SPDesktop::updateCanvasNow()
1360   _widget->requestCanvasUpdateAndWait();
1363 /**
1364  * Associate document with desktop.
1365  */
1366 void
1367 SPDesktop::setDocument (SPDocument *doc)
1369     if (this->doc() && doc) {
1370         namedview->hide(this);
1371         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1372     }
1374     if (_layer_hierarchy) {
1375         _layer_hierarchy->clear();
1376         delete _layer_hierarchy;
1377     }
1378     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1379     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1380     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1381     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1382     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1384     /* setup EventLog */
1385     event_log = new Inkscape::EventLog(doc);
1386     doc->addUndoObserver(*event_log);
1388     _commit_connection.disconnect();
1389     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1391     /// \todo fixme: This condition exists to make sure the code
1392     /// inside is NOT called on initialization, only on replacement. But there
1393     /// are surely more safe methods to accomplish this.
1394     // TODO since the comment had reversed logic, check the intent of this block of code:
1395     if (drawing) {
1396         NRArenaItem *ai = 0;
1398         namedview = sp_document_namedview (doc, NULL);
1399         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1400         number = namedview->getViewCount();
1402         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1403                 SP_CANVAS_ARENA (drawing)->arena,
1404                 dkey,
1405                 SP_ITEM_SHOW_DISPLAY);
1406         if (ai) {
1407             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1408         }
1409         namedview->show(this);
1410         /* Ugly hack */
1411         activate_guides (true);
1412         /* Ugly hack */
1413         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1414     }
1416     _document_replaced_signal.emit (this, doc);
1418     View::setDocument (doc);
1421 void
1422 SPDesktop::onStatusMessage
1423 (Inkscape::MessageType type, gchar const *message)
1425     if (_widget) {
1426         _widget->setMessage(type, message);
1427     }
1430 void
1431 SPDesktop::onDocumentURISet (gchar const* uri)
1433     _widget->setTitle(uri);
1436 /**
1437  * Resized callback.
1438  */
1439 void
1440 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1442     _doc2dt[5] = height;
1443     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1444     Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1445     SP_CTRLRECT(page)->setRectangle(a);
1446     SP_CTRLRECT(page_border)->setRectangle(a);
1450 void
1451 SPDesktop::_onActivate (SPDesktop* dt)
1453     if (!dt->_widget) return;
1454     dt->_widget->activateDesktop();
1457 void
1458 SPDesktop::_onDeactivate (SPDesktop* dt)
1460     if (!dt->_widget) return;
1461     dt->_widget->deactivateDesktop();
1464 void
1465 SPDesktop::_onSelectionModified
1466 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1468     if (!dt->_widget) return;
1469     dt->_widget->updateScrollbars (dt->_d2w.descrim());
1472 static void
1473 _onSelectionChanged
1474 (Inkscape::Selection *selection, SPDesktop *desktop)
1476     /** \todo
1477      * only change the layer for single selections, or what?
1478      * This seems reasonable -- for multiple selections there can be many
1479      * different layers involved.
1480      */
1481     SPItem *item=selection->singleItem();
1482     if (item) {
1483         SPObject *layer=desktop->layerForObject(item);
1484         if ( layer && layer != desktop->currentLayer() ) {
1485             desktop->setCurrentLayer(layer);
1486         }
1487     }
1490 /**
1491  * Calls event handler of current event context.
1492  * \param arena Unused
1493  * \todo fixme
1494  */
1495 static gint
1496 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1498     if (ai) {
1499         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1500         return sp_event_context_item_handler (desktop->event_context, spi, event);
1501     } else {
1502         return sp_event_context_root_handler (desktop->event_context, event);
1503     }
1506 static void
1507 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1508     g_return_if_fail(SP_IS_GROUP(layer));
1509     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1512 /// Callback
1513 static void
1514 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1515     g_return_if_fail(SP_IS_GROUP(layer));
1516     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1519 /// Callback
1520 static void
1521 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1522                                          SPDesktop *desktop)
1524     desktop->_layer_changed_signal.emit (bottom);
1527 /// Called when document is starting to be rebuilt.
1528 static void
1529 _reconstruction_start (SPDesktop * desktop)
1531     // printf("Desktop, starting reconstruction\n");
1532     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1533     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1535     /*
1536     GSList const * selection_objs = desktop->selection->list();
1537     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1539     }
1540     */
1541     desktop->selection->clear();
1543     // printf("Desktop, starting reconstruction end\n");
1546 /// Called when document rebuild is finished.
1547 static void
1548 _reconstruction_finish (SPDesktop * desktop)
1550     // printf("Desktop, finishing reconstruction\n");
1551     if (desktop->_reconstruction_old_layer_id == NULL)
1552         return;
1554     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1555     if (newLayer != NULL)
1556         desktop->setCurrentLayer(newLayer);
1558     g_free(desktop->_reconstruction_old_layer_id);
1559     desktop->_reconstruction_old_layer_id = NULL;
1560     // printf("Desktop, finishing reconstruction end\n");
1561     return;
1564 /**
1565  * Namedview_modified callback.
1566  */
1567 static void
1568 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1570     SPNamedView *nv=SP_NAMEDVIEW(obj);
1572     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1574         /* Recalculate snap distances */
1575         /* FIXME: why is the desktop getting involved in setting up something
1576         ** that is entirely to do with the namedview?
1577         */
1578         _update_snap_distances (desktop);
1580         /* Show/hide page background */
1581         if (nv->pagecolor & 0xff) {
1582             sp_canvas_item_show (desktop->table);
1583             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1584             sp_canvas_item_move_to_z (desktop->table, 0);
1585         } else {
1586             sp_canvas_item_hide (desktop->table);
1587         }
1589         /* Show/hide page border */
1590         if (nv->showborder) {
1591             // show
1592             sp_canvas_item_show (desktop->page_border);
1593             // set color and shadow
1594             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1595             if (nv->pageshadow) {
1596                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1597             }
1598             // place in the z-order stack
1599             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1600                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1601             } else {
1602                 int order = sp_canvas_item_order (desktop->page_border);
1603                 int morder = sp_canvas_item_order (desktop->drawing);
1604                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1605                                     morder - order);
1606             }
1607         } else {
1608                 sp_canvas_item_hide (desktop->page_border);
1609                 if (nv->pageshadow) {
1610                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1611                 }
1612         }
1614         /* Show/hide page shadow */
1615         if (nv->showpageshadow && nv->pageshadow) {
1616             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1617         } else {
1618             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1619         }
1621         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1622             (SP_RGBA32_R_U(nv->pagecolor) +
1623              SP_RGBA32_G_U(nv->pagecolor) +
1624              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1625             // the background color is light or transparent, use black outline
1626             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1627         } else { // use white outline
1628             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1629         }
1630     }
1633 /**
1634  * Callback to reset snapper's distances.
1635  */
1636 static void
1637 _update_snap_distances (SPDesktop *desktop)
1639     SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1641     SPNamedView &nv = *desktop->namedview;
1643     //tell all grid snappers
1644     for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1645         Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1646         grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
1647                                                                       *nv.gridtoleranceunit,
1648                                                                       px));
1649     }
1651     nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
1652                                                                        *nv.guidetoleranceunit,
1653                                                                        px));
1654     nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
1655                                                                         *nv.objecttoleranceunit,
1656                                                                         px));
1660 Geom::Matrix SPDesktop::w2d() const
1662     return _w2d;
1665 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1667     return p * _w2d;
1670 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1672     return p * _d2w;
1675 Geom::Matrix SPDesktop::doc2dt() const
1677     return _doc2dt;
1680 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1682     return p * _doc2dt;
1685 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1687     return p * _doc2dt.inverse();
1691 /**
1692  * Pop event context from desktop's context stack. Never used.
1693  */
1694 // void
1695 // SPDesktop::pop_event_context (unsigned int key)
1696 // {
1697 //    SPEventContext *ec = NULL;
1698 //
1699 //    if (event_context && event_context->key == key) {
1700 //        g_return_if_fail (event_context);
1701 //        g_return_if_fail (event_context->next);
1702 //        ec = event_context;
1703 //        sp_event_context_deactivate (ec);
1704 //        event_context = ec->next;
1705 //        sp_event_context_activate (event_context);
1706 //        _event_context_changed_signal.emit (this, ec);
1707 //    }
1708 //
1709 //    SPEventContext *ref = event_context;
1710 //    while (ref && ref->next && ref->next->key != key)
1711 //        ref = ref->next;
1712 //
1713 //    if (ref && ref->next) {
1714 //        ec = ref->next;
1715 //        ref->next = ec->next;
1716 //    }
1717 //
1718 //    if (ec) {
1719 //        sp_event_context_finish (ec);
1720 //        g_object_unref (G_OBJECT (ec));
1721 //    }
1722 // }
1724 /*
1725   Local Variables:
1726   mode:c++
1727   c-file-style:"stroustrup"
1728   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1729   indent-tabs-mode:nil
1730   fill-column:99
1731   End:
1732 */
1733 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :