Code

Added the ability to toggle a layer "solo". Fixes bug #171530.
[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 "macros.h"
61 #include "inkscape-private.h"
62 #include "desktop.h"
63 #include "desktop-events.h"
64 #include "desktop-handles.h"
65 #include "document.h"
66 #include "message-stack.h"
67 #include "selection.h"
68 #include "select-context.h"
69 #include "sp-namedview.h"
70 #include "color.h"
71 #include "sp-item-group.h"
72 #include "prefs-utils.h"
73 #include "object-hierarchy.h"
74 #include "helper/units.h"
75 #include "display/canvas-arena.h"
76 #include "display/nr-arena.h"
77 #include "display/gnome-canvas-acetate.h"
78 #include "display/sodipodi-ctrlrect.h"
79 #include "display/sp-canvas-util.h"
80 #include "display/canvas-temporary-item-list.h"
81 #include "display/snap-indicator.h"
82 #include "libnr/nr-matrix-div.h"
83 #include "libnr/nr-rect-ops.h"
84 #include "ui/dialog/dialog-manager.h"
85 #include "xml/repr.h"
86 #include "message-context.h"
87 #include "device-manager.h"
88 #include "layer-fns.h"
89 #include "layer-manager.h"
90 #include "event-log.h"
91 #include "display/canvas-grid.h"
92 #include "widgets/desktop-widget.h"
93 #include "box3d-context.h"
95 #include "display/sp-canvas.h"
97 namespace Inkscape { namespace XML { class Node; }}
99 // Callback declarations
100 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
101 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
102 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
103 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
104 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
105 static void _reconstruction_start(SPDesktop * desktop);
106 static void _reconstruction_finish(SPDesktop * desktop);
107 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
108 static void _update_snap_distances (SPDesktop *desktop);
110 /**
111  * Return new desktop object.
112  * \pre namedview != NULL.
113  * \pre canvas != NULL.
114  */
115 SPDesktop::SPDesktop() :
116     _dlg_mgr( 0 ),
117     namedview( 0 ),
118     canvas( 0 ),
119     selection( 0 ),
120     event_context( 0 ),
121     layer_manager( 0 ),
122     event_log( 0 ),
123     temporary_item_list( 0 ),
124     snapindicator( 0 ),
125     acetate( 0 ),
126     main( 0 ),
127     gridgroup( 0 ),
128     guides( 0 ),
129     drawing( 0 ),
130     sketch( 0 ),
131     controls( 0 ),
132     tempgroup ( 0 ),
133     table( 0 ),
134     page( 0 ),
135     page_border( 0 ),
136     current( 0 ),
137     zooms_past( 0 ),
138     zooms_future( 0 ),
139     dkey( 0 ),
140     number( 0 ),
141     window_state(0),
142     interaction_disabled_counter( 0 ),
143     waiting_cursor( false ),
144     guides_active( false ),
145     gr_item( 0 ),
146     gr_point_type( 0 ),
147     gr_point_i( 0 ),
148     gr_fill_or_stroke( true ),
149     _layer_hierarchy( 0 ),
150     _reconstruction_old_layer_id( 0 ),
151     _display_mode(Inkscape::RENDERMODE_NORMAL),
152     _saved_display_mode(Inkscape::RENDERMODE_NORMAL),
153     _widget( 0 ),
154     _inkscape( 0 ),
155     _guides_message_context( 0 ),
156     _active( false ),
157     _w2d(),
158     _d2w(),
159     _doc2dt( NR::Matrix(NR::scale(1, -1)) ),
160     grids_visible( false )
162     _d2w.set_identity();
163     _w2d.set_identity();
165     selection = Inkscape::GC::release( new Inkscape::Selection(this) );
168 void
169 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
171     // Temporary workaround for link order issues:
172     Inkscape::DeviceManager::getManager().getDevices();
174     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
176     current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
178     namedview = nv;
179     canvas = aCanvas;
181     SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
182     /* Kill flicker */
183     sp_document_ensure_up_to_date (document);
185     /* Setup Dialog Manager */
186     _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
188     dkey = sp_item_display_key_new (1);
190     /* Connect document */
191     setDocument (document);
193     number = namedview->getViewCount();
196     /* Setup Canvas */
197     g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
199     SPCanvasGroup *root = sp_canvas_root (canvas);
201     /* Setup adminstrative layers */
202     acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
203     g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
204     main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
205     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
207     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
208     SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
209     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
210     sp_canvas_item_move_to_z (table, 0);
212     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
213     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
214     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
216     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
217     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
219     SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
221     if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
222         // Start in outline mode
223         setDisplayModeOutline();
224     } else {
225         // Start in normal mode, default
226         setDisplayModeNormal();
227     }
229     gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
230     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
231     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
232     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
233     tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
235     /* Push select tool to the bottom of stack */
236     /** \todo
237      * FIXME: this is the only call to this.  Everything else seems to just
238      * call "set" instead of "push".  Can we assume that there is only one
239      * context ever?
240      */
241     push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
243     // display rect and zoom are now handled in sp_desktop_widget_realize()
245     NR::Rect const d(NR::Point(0.0, 0.0),
246                      NR::Point(sp_document_width(document), sp_document_height(document)));
248     SP_CTRLRECT(page)->setRectangle(d);
249     SP_CTRLRECT(page_border)->setRectangle(d);
251     /* the following sets the page shadow on the canvas
252        It was originally set to 5, which is really cheesy!
253        It now is an attribute in the document's namedview. If a value of
254        0 is used, then the constructor for a shadow is not initialized.
255     */
257     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
258         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
259     }
262     /* Connect event for page resize */
263     _doc2dt[5] = sp_document_height (document);
264     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
266     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
268     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
269             SP_CANVAS_ARENA (drawing)->arena,
270             dkey,
271             SP_ITEM_SHOW_DISPLAY);
272     if (ai) {
273         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
274         nr_arena_item_unref (ai);
275     }
277     namedview->show(this);
278     /* Ugly hack */
279     activate_guides (true);
280     /* Ugly hack */
281     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
283 /* Set up notification of rebuilding the document, this allows
284        for saving object related settings in the document. */
285     _reconstruction_start_connection =
286         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
287     _reconstruction_finish_connection =
288         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
289     _reconstruction_old_layer_id = NULL;
291     // ?
292     // sp_active_desktop_set (desktop);
293     _inkscape = INKSCAPE;
295     _activate_connection = _activate_signal.connect(
296         sigc::bind(
297             sigc::ptr_fun(_onActivate),
298             this
299         )
300     );
301      _deactivate_connection = _deactivate_signal.connect(
302         sigc::bind(
303             sigc::ptr_fun(_onDeactivate),
304             this
305         )
306     );
308     _sel_modified_connection = selection->connectModified(
309         sigc::bind(
310             sigc::ptr_fun(&_onSelectionModified),
311             this
312         )
313     );
314     _sel_changed_connection = selection->connectChanged(
315         sigc::bind(
316             sigc::ptr_fun(&_onSelectionChanged),
317             this
318         )
319     );
322     /* setup LayerManager */
323     //   (Setting up after the connections are all in place, as it may use some of them)
324     layer_manager = new Inkscape::LayerManager( this );
326     showGrids(namedview->grids_visible, false);
328     temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
329     snapindicator = new Inkscape::Display::SnapIndicator ( this );
333 void SPDesktop::destroy()
335     if (snapindicator) {
336         delete snapindicator;
337         snapindicator = NULL;
338     }
339     if (temporary_item_list) {
340         delete temporary_item_list;
341         temporary_item_list = NULL;
342     }
344     namedview->hide(this);
346     _activate_connection.disconnect();
347     _deactivate_connection.disconnect();
348     _sel_modified_connection.disconnect();
349     _sel_changed_connection.disconnect();
350     _modified_connection.disconnect();
351     _commit_connection.disconnect();
352     _reconstruction_start_connection.disconnect();
353     _reconstruction_finish_connection.disconnect();
355     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
356     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
357     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
359     while (event_context) {
360         SPEventContext *ec = event_context;
361         event_context = ec->next;
362         sp_event_context_finish (ec);
363         g_object_unref (G_OBJECT (ec));
364     }
366     if (_layer_hierarchy) {
367         delete _layer_hierarchy;
368 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
369     }
371     if (layer_manager) {
372         delete layer_manager;
373         layer_manager = NULL;
374     }
376     if (_inkscape) {
377         _inkscape = NULL;
378     }
380     if (drawing) {
381         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
382         drawing = NULL;
383     }
385     delete _guides_message_context;
386     _guides_message_context = NULL;
388     g_list_free (zooms_past);
389     g_list_free (zooms_future);
392 SPDesktop::~SPDesktop() {}
394 //--------------------------------------------------------------------
395 /* Public methods */
398 /** Note that lifetime is measured in milliseconds
399 * it is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
400 * The return value should only be used as argument for SPDesktop::remove_temporary_canvasitem, because the object might be deleted already.
401 * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
402 */
403 Inkscape::Display::TemporaryItem *
404 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
406     if (move_to_bottom) {
407         sp_canvas_item_move_to_z(item, 0);
408     }
410     return temporary_item_list->add_item(item, lifetime);
413 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
414 */
415 void
416 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
418     // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
419     if (tempitem && temporary_item_list) {
420         temporary_item_list->delete_item(tempitem);
421     }
424 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
425     SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
426     canvas->rendermode = mode;
427     _display_mode = mode;
428     if (mode != Inkscape::RENDERMODE_OUTLINE) {
429         _saved_display_mode = _display_mode;
430     }
431     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
432     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
435 void SPDesktop::displayModeToggle() {
436     if (_display_mode == Inkscape::RENDERMODE_OUTLINE) {
437         _setDisplayMode(_saved_display_mode);
438     } else {
439         _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
440     }
443 /**
444  * Returns current root (=bottom) layer.
445  */
446 SPObject *SPDesktop::currentRoot() const
448     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
451 /**
452  * Returns current top layer.
453  */
454 SPObject *SPDesktop::currentLayer() const
456     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
459 /**
460  * Sets the current layer of the desktop.
461  *
462  * Make \a object the top layer.
463  */
464 void SPDesktop::setCurrentLayer(SPObject *object) {
465     g_return_if_fail(SP_IS_GROUP(object));
466     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
467     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
468     _layer_hierarchy->setBottom(object);
471 void SPDesktop::toggleLayerSolo(SPObject *object) {
472     g_return_if_fail(SP_IS_GROUP(object));
473     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
475     bool othersShowing = false;
476     std::vector<SPObject*> layers;
477     for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
478         layers.push_back(obj);
479         othersShowing |= !SP_ITEM(obj)->isHidden();
480     }
481     for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
482         layers.push_back(obj);
483         othersShowing |= !SP_ITEM(obj)->isHidden();
484     }
487     if ( SP_ITEM(object)->isHidden() ) {
488         SP_ITEM(object)->setHidden(false);
489     }
491     for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
492         SP_ITEM(*it)->setHidden(othersShowing);
493     }
496 /**
497  * Return layer that contains \a object.
498  */
499 SPObject *SPDesktop::layerForObject(SPObject *object) {
500     g_return_val_if_fail(object != NULL, NULL);
502     SPObject *root=currentRoot();
503     object = SP_OBJECT_PARENT(object);
504     while ( object && object != root && !isLayer(object) ) {
505         object = SP_OBJECT_PARENT(object);
506     }
507     return object;
510 /**
511  * True if object is a layer.
512  */
513 bool SPDesktop::isLayer(SPObject *object) const {
514     return ( SP_IS_GROUP(object)
515              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
516                   == SPGroup::LAYER ) );
519 /**
520  * True if desktop viewport fully contains \a item's bbox.
521  */
522 bool SPDesktop::isWithinViewport (SPItem *item) const
524     NR::Rect const viewport = get_display_area();
525     NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
526     if (bbox) {
527         return viewport.contains(*bbox);
528     } else {
529         return true;
530     }
533 ///
534 bool SPDesktop::itemIsHidden(SPItem const *item) const {
535     return item->isHidden(this->dkey);
538 /**
539  * Set activate property of desktop; emit signal if changed.
540  */
541 void
542 SPDesktop::set_active (bool new_active)
544     if (new_active != _active) {
545         _active = new_active;
546         if (new_active) {
547             _activate_signal.emit();
548         } else {
549             _deactivate_signal.emit();
550         }
551     }
554 /**
555  * Set activate status of current desktop's named view.
556  */
557 void
558 SPDesktop::activate_guides(bool activate)
560     guides_active = activate;
561     namedview->activateGuides(this, activate);
564 /**
565  * Make desktop switch documents.
566  */
567 void
568 SPDesktop::change_document (SPDocument *theDocument)
570     g_return_if_fail (theDocument != NULL);
572     /* unselect everything before switching documents */
573     selection->clear();
575     setDocument (theDocument);
577     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
578        (this can probably be done in a better way) */
579     Gtk::Window *parent = this->getToplevel();
580     g_assert(parent != NULL);
581     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
582     if (dtw) dtw->desktop = this;
583     sp_desktop_widget_update_namedview(dtw);
585     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
586     _document_replaced_signal.emit (this, theDocument);
589 /**
590  * Make desktop switch event contexts.
591  */
592 void
593 SPDesktop::set_event_context (GtkType type, const gchar *config)
595     SPEventContext *ec;
596     while (event_context) {
597         ec = event_context;
598         sp_event_context_deactivate (ec);
599         event_context = ec->next;
600         sp_event_context_finish (ec);
601         g_object_unref (G_OBJECT (ec));
602     }
604     Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
605     ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
606     ec->next = event_context;
607     event_context = ec;
608     sp_event_context_activate (ec);
609     _event_context_changed_signal.emit (this, ec);
612 /**
613  * Push event context onto desktop's context stack.
614  */
615 void
616 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
618     SPEventContext *ref, *ec;
619     Inkscape::XML::Node *repr;
621     if (event_context && event_context->key == key) return;
622     ref = event_context;
623     while (ref && ref->next && ref->next->key != key) ref = ref->next;
624     if (ref && ref->next) {
625         ec = ref->next;
626         ref->next = ec->next;
627         sp_event_context_finish (ec);
628         g_object_unref (G_OBJECT (ec));
629     }
631     if (event_context) sp_event_context_deactivate (event_context);
632     repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
633     ec = sp_event_context_new (type, this, repr, key);
634     ec->next = event_context;
635     event_context = ec;
636     sp_event_context_activate (ec);
637     _event_context_changed_signal.emit (this, ec);
640 /**
641  * Sets the coordinate status to a given point
642  */
643 void
644 SPDesktop::set_coordinate_status (NR::Point p) {
645     _widget->setCoordinateStatus(p);
648 /**
649  * \see sp_document_item_from_list_at_point_bottom()
650  */
651 SPItem *
652 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
654     g_return_val_if_fail (doc() != NULL, NULL);
655     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
658 /**
659  * \see sp_document_item_at_point()
660  */
661 SPItem *
662 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
664     g_return_val_if_fail (doc() != NULL, NULL);
665     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
668 /**
669  * \see sp_document_group_at_point()
670  */
671 SPItem *
672 SPDesktop::group_at_point (NR::Point const p) const
674     g_return_val_if_fail (doc() != NULL, NULL);
675     return sp_document_group_at_point (doc(), dkey, p);
678 /**
679  * \brief  Returns the mouse point in document coordinates; if mouse is
680  * outside the canvas, returns the center of canvas viewpoint
681  */
682 NR::Point
683 SPDesktop::point() const
685     NR::Point p = _widget->getPointer();
686     NR::Point pw = sp_canvas_window_to_world (canvas, p);
687     p = w2d(pw);
689     NR::Rect const r = canvas->getViewbox();
691     NR::Point r0 = w2d(r.min());
692     NR::Point r1 = w2d(r.max());
694     if (p[NR::X] >= r0[NR::X] &&
695         p[NR::X] <= r1[NR::X] &&
696         p[NR::Y] >= r1[NR::Y] &&
697         p[NR::Y] <= r0[NR::Y])
698     {
699         return p;
700     } else {
701         return (r0 + r1) / 2;
702     }
705 /**
706  * Put current zoom data in history list.
707  */
708 void
709 SPDesktop::push_current_zoom (GList **history)
711     NR::Rect const area = get_display_area();
713     NRRect *old_zoom = g_new(NRRect, 1);
714     old_zoom->x0 = area.min()[NR::X];
715     old_zoom->x1 = area.max()[NR::X];
716     old_zoom->y0 = area.min()[NR::Y];
717     old_zoom->y1 = area.max()[NR::Y];
718     if ( *history == NULL
719          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
720                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
721                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
722                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
723     {
724         *history = g_list_prepend (*history, old_zoom);
725     }
728 /**
729  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
730  */
731 void
732 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
734     g_assert(_widget);
736     // save the zoom
737     if (log) {
738         push_current_zoom(&zooms_past);
739         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
740         g_list_free (zooms_future);
741         zooms_future = NULL;
742     }
744     double const cx = 0.5 * (x0 + x1);
745     double const cy = 0.5 * (y0 + y1);
747     NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
749     double scale = expansion(_d2w);
750     double newscale;
751     if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
752         newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
753     } else {
754         newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
755     }
757     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
759     int clear = FALSE;
760     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
761         /* Set zoom factors */
762         _d2w = NR::Matrix(NR::scale(newscale, -newscale));
763         _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
764         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
765         clear = TRUE;
766     }
768     /* Calculate top left corner (in document pixels) */
769     x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
770     y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
772     /* Scroll */
773     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
775     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
776     sp_box3d_context_update_lines(event_context);
778     _widget->updateRulers();
779     _widget->updateScrollbars(expansion(_d2w));
780     _widget->updateZoom();
783 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
785     set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
788 /**
789  * Return viewbox dimensions.
790  */
791 NR::Rect SPDesktop::get_display_area() const
793     NR::Rect const viewbox = canvas->getViewbox();
795     double const scale = _d2w[0];
797     return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
798                     NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
801 /**
802  * Revert back to previous zoom if possible.
803  */
804 void
805 SPDesktop::prev_zoom()
807     if (zooms_past == NULL) {
808         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
809         return;
810     }
812     // push current zoom into forward zooms list
813     push_current_zoom (&zooms_future);
815     // restore previous zoom
816     set_display_area (((NRRect *) zooms_past->data)->x0,
817             ((NRRect *) zooms_past->data)->y0,
818             ((NRRect *) zooms_past->data)->x1,
819             ((NRRect *) zooms_past->data)->y1,
820             0, false);
822     // remove the just-added zoom from the past zooms list
823     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
826 /**
827  * Set zoom to next in list.
828  */
829 void
830 SPDesktop::next_zoom()
832     if (zooms_future == NULL) {
833         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
834         return;
835     }
837     // push current zoom into past zooms list
838     push_current_zoom (&zooms_past);
840     // restore next zoom
841     set_display_area (((NRRect *) zooms_future->data)->x0,
842             ((NRRect *) zooms_future->data)->y0,
843             ((NRRect *) zooms_future->data)->x1,
844             ((NRRect *) zooms_future->data)->y1,
845             0, false);
847     // remove the just-used zoom from the zooms_future list
848     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
851 /**
852  * Zoom to point with absolute zoom factor.
853  */
854 void
855 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
857     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
859     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
860     // this check prevents "sliding" when trying to zoom in at maximum zoom;
861     /// \todo someone please fix calculations properly and remove this hack
862     if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
863         return;
865     NR::Rect const viewbox = canvas->getViewbox();
867     double const width2 = viewbox.dimensions()[NR::X] / zoom;
868     double const height2 = viewbox.dimensions()[NR::Y] / zoom;
870     set_display_area(cx - px * width2,
871                      cy - py * height2,
872                      cx + (1 - px) * width2,
873                      cy + (1 - py) * height2,
874                      0.0);
877 /**
878  * Zoom to center with absolute zoom factor.
879  */
880 void
881 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
883     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
886 /**
887  * Zoom to point with relative zoom factor.
888  */
889 void
890 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
892     NR::Rect const area = get_display_area();
894     if (cx < area.min()[NR::X]) {
895         cx = area.min()[NR::X];
896     }
897     if (cx > area.max()[NR::X]) {
898         cx = area.max()[NR::X];
899     }
900     if (cy < area.min()[NR::Y]) {
901         cy = area.min()[NR::Y];
902     }
903     if (cy > area.max()[NR::Y]) {
904         cy = area.max()[NR::Y];
905     }
907     gdouble const scale = expansion(_d2w) * zoom;
908     double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
909     double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
911     zoom_absolute_keep_point(cx, cy, px, py, scale);
914 /**
915  * Zoom to center with relative zoom factor.
916  */
917 void
918 SPDesktop::zoom_relative (double cx, double cy, double zoom)
920     gdouble scale = expansion(_d2w) * zoom;
921     zoom_absolute (cx, cy, scale);
924 /**
925  * Set display area to origin and current document dimensions.
926  */
927 void
928 SPDesktop::zoom_page()
930     NR::Rect d(NR::Point(0, 0),
931                NR::Point(sp_document_width(doc()), sp_document_height(doc())));
933     if (d.isEmpty(1.0)) {
934         return;
935     }
937     set_display_area(d, 10);
940 /**
941  * Set display area to current document width.
942  */
943 void
944 SPDesktop::zoom_page_width()
946     NR::Rect const a = get_display_area();
948     if (sp_document_width(doc()) < 1.0) {
949         return;
950     }
952     NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
953                NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
955     set_display_area(d, 10);
958 /**
959  * Zoom to selection.
960  */
961 void
962 SPDesktop::zoom_selection()
964     NR::Maybe<NR::Rect> const d = selection->bounds();
966     if ( !d || d->isEmpty(0.1) ) {
967         return;
968     }
970     set_display_area(*d, 10);
973 /**
974  * Tell widget to let zoom widget grab keyboard focus.
975  */
976 void
977 SPDesktop::zoom_grab_focus()
979     _widget->letZoomGrabFocus();
982 /**
983  * Zoom to whole drawing.
984  */
985 void
986 SPDesktop::zoom_drawing()
988     g_return_if_fail (doc() != NULL);
989     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
990     g_return_if_fail (docitem != NULL);
992     NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
994     /* Note that the second condition here indicates that
995     ** there are no items in the drawing.
996     */
997     if ( !d || d->isEmpty(1.0) ) {
998         return;
999     }
1001     set_display_area(*d, 10);
1004 /**
1005  * Scroll canvas by specific coordinate amount in svg coordinates.
1006  */
1007 void
1008 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1010     double scale = expansion(_d2w);
1011     scroll_world(dx*scale, dy*scale, is_scrolling);
1014 /**
1015  * Scroll canvas by specific coordinate amount.
1016  */
1017 void
1018 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1020     g_assert(_widget);
1022     NR::Rect const viewbox = canvas->getViewbox();
1024     sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
1026     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1027     sp_box3d_context_update_lines(event_context);
1029     _widget->updateRulers();
1030     _widget->updateScrollbars(expansion(_d2w));
1033 bool
1034 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
1036     gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
1038     // autoscrolldistance is in screen pixels, but the display area is in document units
1039     autoscrolldistance /= expansion(_d2w);
1040     NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
1042     if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
1043         !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y])   ) {
1045         NR::Point const s_w( (*p) * _d2w );
1047         gdouble x_to;
1048         if ((*p)[NR::X] < dbox.min()[NR::X])
1049             x_to = dbox.min()[NR::X];
1050         else if ((*p)[NR::X] > dbox.max()[NR::X])
1051             x_to = dbox.max()[NR::X];
1052         else
1053             x_to = (*p)[NR::X];
1055         gdouble y_to;
1056         if ((*p)[NR::Y] < dbox.min()[NR::Y])
1057             y_to = dbox.min()[NR::Y];
1058         else if ((*p)[NR::Y] > dbox.max()[NR::Y])
1059             y_to = dbox.max()[NR::Y];
1060         else
1061             y_to = (*p)[NR::Y];
1063         NR::Point const d_dt(x_to, y_to);
1064         NR::Point const d_w( d_dt * _d2w );
1065         NR::Point const moved_w( d_w - s_w );
1067         if (autoscrollspeed == 0)
1068             autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
1070         if (autoscrollspeed != 0)
1071             scroll_world (autoscrollspeed * moved_w);
1073         return true;
1074     }
1075     return false;
1078 bool
1079 SPDesktop::is_iconified()
1081     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1084 void
1085 SPDesktop::iconify()
1087     _widget->setIconified();
1090 bool
1091 SPDesktop::is_maximized()
1093     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1096 void
1097 SPDesktop::maximize()
1099     _widget->setMaximized();
1102 bool
1103 SPDesktop::is_fullscreen()
1105     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1108 void
1109 SPDesktop::fullscreen()
1111     _widget->setFullscreen();
1114 void
1115 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1117     _widget->getGeometry (x, y, w, h);
1120 void
1121 SPDesktop::setWindowPosition (NR::Point p)
1123     _widget->setPosition (p);
1126 void
1127 SPDesktop::setWindowSize (gint w, gint h)
1129     _widget->setSize (w, h);
1132 void
1133 SPDesktop::setWindowTransient (void *p, int transient_policy)
1135     _widget->setTransient (p, transient_policy);
1138 Gtk::Window*
1139 SPDesktop::getToplevel( )
1141     return _widget->getWindow();
1144 void
1145 SPDesktop::presentWindow()
1147     _widget->present();
1150 bool
1151 SPDesktop::warnDialog (gchar *text)
1153     return _widget->warnDialog (text);
1156 void
1157 SPDesktop::toggleRulers()
1159     _widget->toggleRulers();
1162 void
1163 SPDesktop::toggleScrollbars()
1165     _widget->toggleScrollbars();
1168 void
1169 SPDesktop::layoutWidget()
1171     _widget->layout();
1174 void
1175 SPDesktop::destroyWidget()
1177     _widget->destroy();
1180 bool
1181 SPDesktop::shutdown()
1183     return _widget->shutdown();
1186 bool SPDesktop::onDeleteUI (GdkEventAny*)
1188     if(shutdown()) 
1189         return true;
1191     destroyWidget();
1192     return false;
1195 /**
1196  *  onWindowStateEvent
1197  *
1198  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1199  *  Since GTK doesn't have a way to query this state information directly, we
1200  *  record it for the desktop here, and also possibly trigger a layout.
1201  */
1202 bool
1203 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1205         // Record the desktop window's state
1206     window_state = event->new_window_state;
1208     // Layout may differ depending on full-screen mode or not
1209     GdkWindowState changed = event->changed_mask;
1210     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1211         layoutWidget();
1212     }
1214         return false;
1217 void
1218 SPDesktop::setToolboxFocusTo (gchar const *label)
1220     _widget->setToolboxFocusTo (label);
1223 void
1224 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1226     _widget->setToolboxAdjustmentValue (id, val);
1229 void
1230 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1232     _widget->setToolboxSelectOneValue (id, val);
1235 bool
1236 SPDesktop::isToolboxButtonActive (gchar const *id)
1238     return _widget->isToolboxButtonActive (id);
1241 void
1242 SPDesktop::emitToolSubselectionChanged(gpointer data)
1244         _tool_subselection_changed.emit(data);
1245         inkscape_subselection_changed (this);
1248 void
1249 SPDesktop::updateNow()
1251   sp_canvas_update_now(canvas);
1254 void
1255 SPDesktop::enableInteraction()
1257   _widget->enableInteraction();
1260 void SPDesktop::disableInteraction()
1262   _widget->disableInteraction();
1265 void SPDesktop::setWaitingCursor()
1267     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1268     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1269     gdk_cursor_unref(waiting);
1270     waiting_cursor = true;
1272     // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1273     // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1274     // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1275     // after the call to setWaitingCursor as it was before
1276     while( Gtk::Main::events_pending() )
1277        Gtk::Main::iteration();
1280 void SPDesktop::clearWaitingCursor()
1282   if (waiting_cursor)
1283       sp_event_context_update_cursor(sp_desktop_event_context(this));
1286 void SPDesktop::toggleColorProfAdjust()
1288     _widget->toggleColorProfAdjust();
1291 void SPDesktop::toggleGrids()
1293     if (namedview->grids) {
1294         if(gridgroup) {
1295             showGrids(!grids_visible);
1296         }
1297     } else {
1298         //there is no grid present at the moment. add a rectangular grid and make it visible
1299         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1300         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1301         showGrids(true);
1302     }
1305 void SPDesktop::showGrids(bool show, bool dirty_document)
1307     grids_visible = show;
1308     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1309     if (show) {
1310         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1311     } else {
1312         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1313     }
1316 void SPDesktop::toggleSnapping()
1318     bool v = namedview->snap_manager.getSnapEnabledGlobally();
1319     Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1320     sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1323 //----------------------------------------------------------------------
1324 // Callback implementations. The virtual ones are connected by the view.
1326 void
1327 SPDesktop::onPositionSet (double x, double y)
1329     _widget->viewSetPosition (NR::Point(x,y));
1332 void
1333 SPDesktop::onResized (double /*x*/, double /*y*/)
1335    // Nothing called here
1338 /**
1339  * Redraw callback; queues Gtk redraw; connected by View.
1340  */
1341 void
1342 SPDesktop::onRedrawRequested ()
1344     if (main) {
1345         _widget->requestCanvasUpdate();
1346     }
1349 void
1350 SPDesktop::updateCanvasNow()
1352   _widget->requestCanvasUpdateAndWait();
1355 /**
1356  * Associate document with desktop.
1357  */
1358 void
1359 SPDesktop::setDocument (SPDocument *doc)
1361     if (this->doc() && doc) {
1362         namedview->hide(this);
1363         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1364     }
1366     if (_layer_hierarchy) {
1367         _layer_hierarchy->clear();
1368         delete _layer_hierarchy;
1369     }
1370     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1371     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1372     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1373     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1374     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1376     /* setup EventLog */
1377     event_log = new Inkscape::EventLog(doc);
1378     doc->addUndoObserver(*event_log);
1380     _commit_connection.disconnect();
1381     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1383     /// \todo fixme: This condition exists to make sure the code
1384     /// inside is NOT called on initialization, only on replacement. But there
1385     /// are surely more safe methods to accomplish this.
1386     // TODO since the comment had reversed logic, check the intent of this block of code:
1387     if (drawing) {
1388         NRArenaItem *ai = 0;
1390         namedview = sp_document_namedview (doc, NULL);
1391         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1392         number = namedview->getViewCount();
1394         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1395                 SP_CANVAS_ARENA (drawing)->arena,
1396                 dkey,
1397                 SP_ITEM_SHOW_DISPLAY);
1398         if (ai) {
1399             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1400             nr_arena_item_unref (ai);
1401         }
1402         namedview->show(this);
1403         /* Ugly hack */
1404         activate_guides (true);
1405         /* Ugly hack */
1406         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1407     }
1409     _document_replaced_signal.emit (this, doc);
1411     View::setDocument (doc);
1414 void
1415 SPDesktop::onStatusMessage
1416 (Inkscape::MessageType type, gchar const *message)
1418     if (_widget) {
1419         _widget->setMessage(type, message);
1420     }
1423 void
1424 SPDesktop::onDocumentURISet (gchar const* uri)
1426     _widget->setTitle(uri);
1429 /**
1430  * Resized callback.
1431  */
1432 void
1433 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1435     _doc2dt[5] = height;
1436     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1437     NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1438     SP_CTRLRECT(page)->setRectangle(a);
1439     SP_CTRLRECT(page_border)->setRectangle(a);
1443 void
1444 SPDesktop::_onActivate (SPDesktop* dt)
1446     if (!dt->_widget) return;
1447     dt->_widget->activateDesktop();
1450 void
1451 SPDesktop::_onDeactivate (SPDesktop* dt)
1453     if (!dt->_widget) return;
1454     dt->_widget->deactivateDesktop();
1457 void
1458 SPDesktop::_onSelectionModified
1459 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1461     if (!dt->_widget) return;
1462     dt->_widget->updateScrollbars (expansion(dt->_d2w));
1465 static void
1466 _onSelectionChanged
1467 (Inkscape::Selection *selection, SPDesktop *desktop)
1469     /** \todo
1470      * only change the layer for single selections, or what?
1471      * This seems reasonable -- for multiple selections there can be many
1472      * different layers involved.
1473      */
1474     SPItem *item=selection->singleItem();
1475     if (item) {
1476         SPObject *layer=desktop->layerForObject(item);
1477         if ( layer && layer != desktop->currentLayer() ) {
1478             desktop->setCurrentLayer(layer);
1479         }
1480     }
1483 /**
1484  * Calls event handler of current event context.
1485  * \param arena Unused
1486  * \todo fixme
1487  */
1488 static gint
1489 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1491     if (ai) {
1492         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1493         return sp_event_context_item_handler (desktop->event_context, spi, event);
1494     } else {
1495         return sp_event_context_root_handler (desktop->event_context, event);
1496     }
1499 static void
1500 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1501     g_return_if_fail(SP_IS_GROUP(layer));
1502     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1505 /// Callback
1506 static void
1507 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1508     g_return_if_fail(SP_IS_GROUP(layer));
1509     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1512 /// Callback
1513 static void
1514 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1515                                          SPDesktop *desktop)
1517     desktop->_layer_changed_signal.emit (bottom);
1520 /// Called when document is starting to be rebuilt.
1521 static void
1522 _reconstruction_start (SPDesktop * desktop)
1524     // printf("Desktop, starting reconstruction\n");
1525     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1526     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1528     /*
1529     GSList const * selection_objs = desktop->selection->list();
1530     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1532     }
1533     */
1534     desktop->selection->clear();
1536     // printf("Desktop, starting reconstruction end\n");
1539 /// Called when document rebuild is finished.
1540 static void
1541 _reconstruction_finish (SPDesktop * desktop)
1543     // printf("Desktop, finishing reconstruction\n");
1544     if (desktop->_reconstruction_old_layer_id == NULL)
1545         return;
1547     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1548     if (newLayer != NULL)
1549         desktop->setCurrentLayer(newLayer);
1551     g_free(desktop->_reconstruction_old_layer_id);
1552     desktop->_reconstruction_old_layer_id = NULL;
1553     // printf("Desktop, finishing reconstruction end\n");
1554     return;
1557 /**
1558  * Namedview_modified callback.
1559  */
1560 static void
1561 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1563     SPNamedView *nv=SP_NAMEDVIEW(obj);
1565     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1567         /* Recalculate snap distances */
1568         /* FIXME: why is the desktop getting involved in setting up something
1569         ** that is entirely to do with the namedview?
1570         */
1571         _update_snap_distances (desktop);
1573         /* Show/hide page background */
1574         if (nv->pagecolor & 0xff) {
1575             sp_canvas_item_show (desktop->table);
1576             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1577             sp_canvas_item_move_to_z (desktop->table, 0);
1578         } else {
1579             sp_canvas_item_hide (desktop->table);
1580         }
1582         /* Show/hide page border */
1583         if (nv->showborder) {
1584             // show
1585             sp_canvas_item_show (desktop->page_border);
1586             // set color and shadow
1587             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1588             if (nv->pageshadow) {
1589                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1590             }
1591             // place in the z-order stack
1592             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1593                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1594             } else {
1595                 int order = sp_canvas_item_order (desktop->page_border);
1596                 int morder = sp_canvas_item_order (desktop->drawing);
1597                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1598                                     morder - order);
1599             }
1600         } else {
1601                 sp_canvas_item_hide (desktop->page_border);
1602                 if (nv->pageshadow) {
1603                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1604                 }
1605         }
1607         /* Show/hide page shadow */
1608         if (nv->showpageshadow && nv->pageshadow) {
1609             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1610         } else {
1611             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1612         }
1614         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1615             (SP_RGBA32_R_U(nv->pagecolor) +
1616              SP_RGBA32_G_U(nv->pagecolor) +
1617              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1618             // the background color is light or transparent, use black outline
1619             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1620         } else { // use white outline
1621             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1622         }
1623     }
1626 /**
1627  * Callback to reset snapper's distances.
1628  */
1629 static void
1630 _update_snap_distances (SPDesktop *desktop)
1632     SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1634     SPNamedView &nv = *desktop->namedview;
1636     //tell all grid snappers
1637     for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1638         Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1639         grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
1640                                                                       *nv.gridtoleranceunit,
1641                                                                       px));
1642     }
1644     nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
1645                                                                        *nv.guidetoleranceunit,
1646                                                                        px));
1647     nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
1648                                                                         *nv.objecttoleranceunit,
1649                                                                         px));
1653 NR::Matrix SPDesktop::w2d() const
1655     return _w2d;
1658 NR::Point SPDesktop::w2d(NR::Point const &p) const
1660     return p * _w2d;
1663 NR::Point SPDesktop::d2w(NR::Point const &p) const
1665     return p * _d2w;
1668 NR::Matrix SPDesktop::doc2dt() const
1670     return _doc2dt;
1673 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1675     return p * _doc2dt;
1678 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1680     return p / _doc2dt;
1684 /**
1685  * Pop event context from desktop's context stack. Never used.
1686  */
1687 // void
1688 // SPDesktop::pop_event_context (unsigned int key)
1689 // {
1690 //    SPEventContext *ec = NULL;
1691 //
1692 //    if (event_context && event_context->key == key) {
1693 //        g_return_if_fail (event_context);
1694 //        g_return_if_fail (event_context->next);
1695 //        ec = event_context;
1696 //        sp_event_context_deactivate (ec);
1697 //        event_context = ec->next;
1698 //        sp_event_context_activate (event_context);
1699 //        _event_context_changed_signal.emit (this, ec);
1700 //    }
1701 //
1702 //    SPEventContext *ref = event_context;
1703 //    while (ref && ref->next && ref->next->key != key)
1704 //        ref = ref->next;
1705 //
1706 //    if (ref && ref->next) {
1707 //        ec = ref->next;
1708 //        ref->next = ec->next;
1709 //    }
1710 //
1711 //    if (ec) {
1712 //        sp_event_context_finish (ec);
1713 //        g_object_unref (G_OBJECT (ec));
1714 //    }
1715 // }
1717 /*
1718   Local Variables:
1719   mode:c++
1720   c-file-style:"stroustrup"
1721   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1722   indent-tabs-mode:nil
1723   fill-column:99
1724   End:
1725 */
1726 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :