Code

Sorry, I got off on a branch and ended up with a bunch of things. I'm just going...
[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         _focusMode(false),
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.setIdentity();
163     _w2d.setIdentity();
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(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
209     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
210     sp_canvas_item_move_to_z (table, 0);
212     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
213     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
214     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
216     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
217     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
219     SP_CANVAS_ARENA (drawing)->arena->delta = prefs_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     Geom::Rect const d(Geom::Point(0.0, 0.0),
246                        Geom::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     }
276     namedview->show(this);
277     /* Ugly hack */
278     activate_guides (true);
279     /* Ugly hack */
280     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
282 /* Set up notification of rebuilding the document, this allows
283        for saving object related settings in the document. */
284     _reconstruction_start_connection =
285         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
286     _reconstruction_finish_connection =
287         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
288     _reconstruction_old_layer_id = NULL;
290     // ?
291     // sp_active_desktop_set (desktop);
292     _inkscape = INKSCAPE;
294     _activate_connection = _activate_signal.connect(
295         sigc::bind(
296             sigc::ptr_fun(_onActivate),
297             this
298         )
299     );
300      _deactivate_connection = _deactivate_signal.connect(
301         sigc::bind(
302             sigc::ptr_fun(_onDeactivate),
303             this
304         )
305     );
307     _sel_modified_connection = selection->connectModified(
308         sigc::bind(
309             sigc::ptr_fun(&_onSelectionModified),
310             this
311         )
312     );
313     _sel_changed_connection = selection->connectChanged(
314         sigc::bind(
315             sigc::ptr_fun(&_onSelectionChanged),
316             this
317         )
318     );
321     /* setup LayerManager */
322     //   (Setting up after the connections are all in place, as it may use some of them)
323     layer_manager = new Inkscape::LayerManager( this );
325     showGrids(namedview->grids_visible, false);
327     temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
328     snapindicator = new Inkscape::Display::SnapIndicator ( this );
332 void SPDesktop::destroy()
334     if (snapindicator) {
335         delete snapindicator;
336         snapindicator = NULL;
337     }
338     if (temporary_item_list) {
339         delete temporary_item_list;
340         temporary_item_list = NULL;
341     }
343     if (selection) {
344         delete selection;
345         selection = NULL;
346     }
348     namedview->hide(this);
350     _activate_connection.disconnect();
351     _deactivate_connection.disconnect();
352     _sel_modified_connection.disconnect();
353     _sel_changed_connection.disconnect();
354     _modified_connection.disconnect();
355     _commit_connection.disconnect();
356     _reconstruction_start_connection.disconnect();
357     _reconstruction_finish_connection.disconnect();
359     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
360     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
361     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
363     while (event_context) {
364         SPEventContext *ec = event_context;
365         event_context = ec->next;
366         sp_event_context_finish (ec);
367         g_object_unref (G_OBJECT (ec));
368     }
370     if (_layer_hierarchy) {
371         delete _layer_hierarchy;
372 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
373     }
375     if (layer_manager) {
376         delete layer_manager;
377         layer_manager = NULL;
378     }
380     if (_inkscape) {
381         _inkscape = NULL;
382     }
384     if (drawing) {
385         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
386         drawing = NULL;
387     }
389     delete _guides_message_context;
390     _guides_message_context = NULL;
392     g_list_free (zooms_past);
393     g_list_free (zooms_future);
396 SPDesktop::~SPDesktop() {}
398 //--------------------------------------------------------------------
399 /* Public methods */
402 /* These methods help for temporarily showing things on-canvas.
403  * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
404  * is when you want to prematurely remove the item from the canvas, by calling
405  * desktop->remove_temporary_canvasitem(tempitem).
406  */
407 /** Note that lifetime is measured in milliseconds
408  * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
409  * delete the object for you and the reference will become invalid without you knowing it.
410  * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
411  * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
412  * because the object might be deleted already without you knowing it.
413  * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
414  */
415 Inkscape::Display::TemporaryItem *
416 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
418     if (move_to_bottom) {
419         sp_canvas_item_move_to_z(item, 0);
420     }
422     return temporary_item_list->add_item(item, lifetime);
425 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
426 */
427 void
428 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
430     // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
431     if (tempitem && temporary_item_list) {
432         temporary_item_list->delete_item(tempitem);
433     }
436 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
437     SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
438     canvas->rendermode = mode;
439     _display_mode = mode;
440     if (mode != Inkscape::RENDERMODE_OUTLINE) {
441         _saved_display_mode = _display_mode;
442     }
443     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
444     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
447 void SPDesktop::displayModeToggle() {
448     if (_display_mode == Inkscape::RENDERMODE_OUTLINE) {
449         _setDisplayMode(_saved_display_mode);
450     } else {
451         _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
452     }
455 /**
456  * Returns current root (=bottom) layer.
457  */
458 SPObject *SPDesktop::currentRoot() const
460     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
463 /**
464  * Returns current top layer.
465  */
466 SPObject *SPDesktop::currentLayer() const
468     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
471 /**
472  * Sets the current layer of the desktop.
473  *
474  * Make \a object the top layer.
475  */
476 void SPDesktop::setCurrentLayer(SPObject *object) {
477     g_return_if_fail(SP_IS_GROUP(object));
478     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
479     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
480     _layer_hierarchy->setBottom(object);
483 void SPDesktop::toggleLayerSolo(SPObject *object) {
484     g_return_if_fail(SP_IS_GROUP(object));
485     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
487     bool othersShowing = false;
488     std::vector<SPObject*> layers;
489     for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
490         layers.push_back(obj);
491         othersShowing |= !SP_ITEM(obj)->isHidden();
492     }
493     for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
494         layers.push_back(obj);
495         othersShowing |= !SP_ITEM(obj)->isHidden();
496     }
499     if ( SP_ITEM(object)->isHidden() ) {
500         SP_ITEM(object)->setHidden(false);
501     }
503     for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
504         SP_ITEM(*it)->setHidden(othersShowing);
505     }
508 /**
509  * Return layer that contains \a object.
510  */
511 SPObject *SPDesktop::layerForObject(SPObject *object) {
512     g_return_val_if_fail(object != NULL, NULL);
514     SPObject *root=currentRoot();
515     object = SP_OBJECT_PARENT(object);
516     while ( object && object != root && !isLayer(object) ) {
517         object = SP_OBJECT_PARENT(object);
518     }
519     return object;
522 /**
523  * True if object is a layer.
524  */
525 bool SPDesktop::isLayer(SPObject *object) const {
526     return ( SP_IS_GROUP(object)
527              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
528                   == SPGroup::LAYER ) );
531 /**
532  * True if desktop viewport fully contains \a item's bbox.
533  */
534 bool SPDesktop::isWithinViewport (SPItem *item) const
536     Geom::Rect const viewport = get_display_area();
537     boost::optional<NR::Rect> const bbox = sp_item_bbox_desktop(item);
538     if (bbox) {
539         return viewport.contains(to_2geom(*bbox));
540     } else {
541         return true;
542     }
545 ///
546 bool SPDesktop::itemIsHidden(SPItem const *item) const {
547     return item->isHidden(this->dkey);
550 /**
551  * Set activate property of desktop; emit signal if changed.
552  */
553 void
554 SPDesktop::set_active (bool new_active)
556     if (new_active != _active) {
557         _active = new_active;
558         if (new_active) {
559             _activate_signal.emit();
560         } else {
561             _deactivate_signal.emit();
562         }
563     }
566 /**
567  * Set activate status of current desktop's named view.
568  */
569 void
570 SPDesktop::activate_guides(bool activate)
572     guides_active = activate;
573     namedview->activateGuides(this, activate);
576 /**
577  * Make desktop switch documents.
578  */
579 void
580 SPDesktop::change_document (SPDocument *theDocument)
582     g_return_if_fail (theDocument != NULL);
584     /* unselect everything before switching documents */
585     selection->clear();
587     setDocument (theDocument);
589     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
590        (this can probably be done in a better way) */
591     Gtk::Window *parent = this->getToplevel();
592     g_assert(parent != NULL);
593     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
594     if (dtw) dtw->desktop = this;
595     sp_desktop_widget_update_namedview(dtw);
597     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
598     _document_replaced_signal.emit (this, theDocument);
601 /**
602  * Make desktop switch event contexts.
603  */
604 void
605 SPDesktop::set_event_context (GtkType type, const gchar *config)
607     SPEventContext *ec;
608     while (event_context) {
609         ec = event_context;
610         sp_event_context_deactivate (ec);
611         event_context = ec->next;
612         sp_event_context_finish (ec);
613         g_object_unref (G_OBJECT (ec));
614     }
616     Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
617     ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
618     ec->next = event_context;
619     event_context = ec;
620     sp_event_context_activate (ec);
621     _event_context_changed_signal.emit (this, ec);
624 /**
625  * Push event context onto desktop's context stack.
626  */
627 void
628 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
630     SPEventContext *ref, *ec;
631     Inkscape::XML::Node *repr;
633     if (event_context && event_context->key == key) return;
634     ref = event_context;
635     while (ref && ref->next && ref->next->key != key) ref = ref->next;
636     if (ref && ref->next) {
637         ec = ref->next;
638         ref->next = ec->next;
639         sp_event_context_finish (ec);
640         g_object_unref (G_OBJECT (ec));
641     }
643     if (event_context) sp_event_context_deactivate (event_context);
644     repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
645     ec = sp_event_context_new (type, this, repr, key);
646     ec->next = event_context;
647     event_context = ec;
648     sp_event_context_activate (ec);
649     _event_context_changed_signal.emit (this, ec);
652 /**
653  * Sets the coordinate status to a given point
654  */
655 void
656 SPDesktop::set_coordinate_status (Geom::Point p) {
657     _widget->setCoordinateStatus(p);
660 /**
661  * \see sp_document_item_from_list_at_point_bottom()
662  */
663 SPItem *
664 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
666     g_return_val_if_fail (doc() != NULL, NULL);
667     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
670 /**
671  * \see sp_document_item_at_point()
672  */
673 SPItem *
674 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
676     g_return_val_if_fail (doc() != NULL, NULL);
677     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
680 /**
681  * \see sp_document_group_at_point()
682  */
683 SPItem *
684 SPDesktop::group_at_point (Geom::Point const p) const
686     g_return_val_if_fail (doc() != NULL, NULL);
687     return sp_document_group_at_point (doc(), dkey, p);
690 /**
691  * \brief  Returns the mouse point in document coordinates; if mouse is
692  * outside the canvas, returns the center of canvas viewpoint
693  */
694 Geom::Point
695 SPDesktop::point() const
697     Geom::Point p = _widget->getPointer();
698     Geom::Point pw = sp_canvas_window_to_world (canvas, p);
699     p = w2d(pw);
701     Geom::Rect const r = canvas->getViewbox();
703     Geom::Point r0 = w2d(r.min());
704     Geom::Point r1 = w2d(r.max());
706     if (p[Geom::X] >= r0[Geom::X] &&
707         p[Geom::X] <= r1[Geom::X] &&
708         p[Geom::Y] >= r1[Geom::Y] &&
709         p[Geom::Y] <= r0[Geom::Y])
710     {
711         return p;
712     } else {
713         return (r0 + r1) / 2;
714     }
717 /**
718  * Put current zoom data in history list.
719  */
720 void
721 SPDesktop::push_current_zoom (GList **history)
723     Geom::Rect const area = get_display_area();
725     NRRect *old_zoom = g_new(NRRect, 1);
726     old_zoom->x0 = area.min()[NR::X];
727     old_zoom->x1 = area.max()[NR::X];
728     old_zoom->y0 = area.min()[NR::Y];
729     old_zoom->y1 = area.max()[NR::Y];
730     if ( *history == NULL
731          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
732                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
733                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
734                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
735     {
736         *history = g_list_prepend (*history, old_zoom);
737     }
740 /**
741  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
742  */
743 void
744 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
746     g_assert(_widget);
748     // save the zoom
749     if (log) {
750         push_current_zoom(&zooms_past);
751         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
752         g_list_free (zooms_future);
753         zooms_future = NULL;
754     }
756     double const cx = 0.5 * (x0 + x1);
757     double const cy = 0.5 * (y0 + y1);
759     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
760     Geom::Rect viewbox = canvas->getViewbox();
761     viewbox.expandBy(border);
763     double scale = _d2w.descrim();
764     double newscale;
765     if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
766         newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
767     } else {
768         newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
769     }
771     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
773     int clear = FALSE;
774     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
775         /* Set zoom factors */
776         _d2w = NR::Matrix(NR::scale(newscale, -newscale));
777         _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
778         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
779         clear = TRUE;
780     }
782     /* Calculate top left corner (in document pixels) */
783     x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
784     y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
786     /* Scroll */
787     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
789     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
790     sp_box3d_context_update_lines(event_context);
792     _widget->updateRulers();
793     _widget->updateScrollbars(_d2w.descrim());
794     _widget->updateZoom();
797 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
799     set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
802 /**
803  * Return viewbox dimensions.
804  */
805 Geom::Rect SPDesktop::get_display_area() const
807     Geom::Rect const viewbox = canvas->getViewbox();
809     double const scale = _d2w[0];
811     return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
812                       Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
815 /**
816  * Revert back to previous zoom if possible.
817  */
818 void
819 SPDesktop::prev_zoom()
821     if (zooms_past == NULL) {
822         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
823         return;
824     }
826     // push current zoom into forward zooms list
827     push_current_zoom (&zooms_future);
829     // restore previous zoom
830     set_display_area (((NRRect *) zooms_past->data)->x0,
831             ((NRRect *) zooms_past->data)->y0,
832             ((NRRect *) zooms_past->data)->x1,
833             ((NRRect *) zooms_past->data)->y1,
834             0, false);
836     // remove the just-added zoom from the past zooms list
837     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
840 /**
841  * Set zoom to next in list.
842  */
843 void
844 SPDesktop::next_zoom()
846     if (zooms_future == NULL) {
847         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
848         return;
849     }
851     // push current zoom into past zooms list
852     push_current_zoom (&zooms_past);
854     // restore next zoom
855     set_display_area (((NRRect *) zooms_future->data)->x0,
856             ((NRRect *) zooms_future->data)->y0,
857             ((NRRect *) zooms_future->data)->x1,
858             ((NRRect *) zooms_future->data)->y1,
859             0, false);
861     // remove the just-used zoom from the zooms_future list
862     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
865 #include "tools-switch.h"
866 #include "node-context.h"
867 #include "shape-editor.h"
868 #include "nodepath.h"
870 /** \brief  Performs a quick zoom into what the user is working on
871         \param  enable  Whether we're going in or out of quick zoom
873 */
874 void 
875 SPDesktop::zoom_quick (bool enable)
877         if (enable == _quick_zoom_enabled) {
878                 return;
879         }
881         if (enable == true) {
882                 _quick_zoom_stored_area = get_display_area();
883                 bool zoomed = false;
885                 if (!zoomed) {
886                         SPItem * singleItem = selection->singleItem();
887                         if (singleItem != NULL && tools_isactive(this, TOOLS_NODES)) {
888                                 SPNodeContext * ncontext = SP_NODE_CONTEXT(event_context);
890                                 Inkscape::NodePath::Path * nodepath = ncontext->shape_editor->get_nodepath();
891                                 // printf("I've got a nodepath, crazy\n");
893                                 Geom::Rect nodes;
894                                 bool firstnode = true;
896                                 if (nodepath->selected) {
897                                         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
898                                            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
899                                                 for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
900                                                    Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
901                                                         if (node->selected) {
902                                                                 // printf("\tSelected node\n");
903                                                                 if (firstnode) {
904                                                                         nodes = Geom::Rect(node->pos, node->pos);
905                                                                         firstnode = false;
906                                                                 } else {
907                                                                         nodes.expandTo(node->pos);
908                                                                 }
910                                                                 if (node->p.other != NULL) {
911                                                                         /* Include previous node pos */
912                                                                         nodes.expandTo(node->p.other->pos);
914                                                                         /* Include previous handle */
915                                                                         if (!sp_node_side_is_line(node, &node->p)) {
916                                                                                 nodes.expandTo(node->p.pos);
917                                                                         }
918                                                                 }
920                                                                 if (node->n.other != NULL) {
921                                                                         /* Include previous node pos */
922                                                                         nodes.expandTo(node->n.other->pos);
924                                                                         /* Include previous handle */
925                                                                         if (!sp_node_side_is_line(node, &node->n)) {
926                                                                                 nodes.expandTo(node->n.pos);
927                                                                         }
928                                                                 }
929                                                         }
930                                                 }
931                                         }
933                                         if (!firstnode && nodes.area() * 2.0 < _quick_zoom_stored_area.area()) {
934                                                 set_display_area(nodes, 10);
935                                                 zoomed = true;
936                                         }
937                                 }
938                         }
939                 }
941                 if (!zoomed) {
942                         boost::optional<Geom::Rect> const d = selection->bounds_2geom();
943                         if (d && !d->isEmpty() && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
944                                 set_display_area(*d, 10);
945                                 zoomed = true;
946                         } 
947                 }
949                 if (!zoomed) {
950                         zoom_relative(_quick_zoom_stored_area.midpoint()[NR::X], _quick_zoom_stored_area.midpoint()[NR::Y], 2.0);
951                         zoomed = true;
952                 }
953         } else {
954                 set_display_area(_quick_zoom_stored_area, 0);
955         }
957         _quick_zoom_enabled = enable;
958         return;
961 /**
962  * Zoom to point with absolute zoom factor.
963  */
964 void
965 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
967     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
969     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
970     // this check prevents "sliding" when trying to zoom in at maximum zoom;
971     /// \todo someone please fix calculations properly and remove this hack
972     if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
973         return;
975     Geom::Rect const viewbox = canvas->getViewbox();
977     double const width2 = viewbox.dimensions()[NR::X] / zoom;
978     double const height2 = viewbox.dimensions()[NR::Y] / zoom;
980     set_display_area(cx - px * width2,
981                      cy - py * height2,
982                      cx + (1 - px) * width2,
983                      cy + (1 - py) * height2,
984                      0.0);
987 /**
988  * Zoom to center with absolute zoom factor.
989  */
990 void
991 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
993     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
996 /**
997  * Zoom to point with relative zoom factor.
998  */
999 void
1000 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
1002     Geom::Rect const area = get_display_area();
1004     if (cx < area.min()[NR::X]) {
1005         cx = area.min()[NR::X];
1006     }
1007     if (cx > area.max()[NR::X]) {
1008         cx = area.max()[NR::X];
1009     }
1010     if (cy < area.min()[NR::Y]) {
1011         cy = area.min()[NR::Y];
1012     }
1013     if (cy > area.max()[NR::Y]) {
1014         cy = area.max()[NR::Y];
1015     }
1017     gdouble const scale = _d2w.descrim() * zoom;
1018     double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
1019     double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
1021     zoom_absolute_keep_point(cx, cy, px, py, scale);
1024 /**
1025  * Zoom to center with relative zoom factor.
1026  */
1027 void
1028 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1030     gdouble scale = _d2w.descrim() * zoom;
1031     zoom_absolute (cx, cy, scale);
1034 /**
1035  * Set display area to origin and current document dimensions.
1036  */
1037 void
1038 SPDesktop::zoom_page()
1040     Geom::Rect d(Geom::Point(0, 0),
1041                  Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1043     // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 1.0; is it safe to ignore it?
1044     if (d.isEmpty()) {
1045         return;
1046     }
1048     set_display_area(d, 10);
1051 /**
1052  * Set display area to current document width.
1053  */
1054 void
1055 SPDesktop::zoom_page_width()
1057     Geom::Rect const a = get_display_area();
1059     if (sp_document_width(doc()) < 1.0) {
1060         return;
1061     }
1063     Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1064                  Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1066     set_display_area(d, 10);
1069 /**
1070  * Zoom to selection.
1071  */
1072 void
1073 SPDesktop::zoom_selection()
1075     boost::optional<Geom::Rect> const d = to_2geom(selection->bounds());
1077     // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 0.1; is it safe to ignore it?
1078     if ( !d || d->isEmpty() ) {
1079         return;
1080     }
1082     set_display_area(*d, 10);
1085 /**
1086  * Tell widget to let zoom widget grab keyboard focus.
1087  */
1088 void
1089 SPDesktop::zoom_grab_focus()
1091     _widget->letZoomGrabFocus();
1094 /**
1095  * Zoom to whole drawing.
1096  */
1097 void
1098 SPDesktop::zoom_drawing()
1100     g_return_if_fail (doc() != NULL);
1101     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1102     g_return_if_fail (docitem != NULL);
1104     boost::optional<Geom::Rect> d = to_2geom(sp_item_bbox_desktop(docitem));
1106     /* Note that the second condition here indicates that
1107     ** there are no items in the drawing.
1108     */
1109     // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 1.0; is it safe to ignore it?
1110     if ( !d || d->isEmpty() ) {
1111         return;
1112     }
1114     set_display_area(*d, 10);
1117 /**
1118  * Scroll canvas by specific coordinate amount in svg coordinates.
1119  */
1120 void
1121 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1123     double scale = _d2w.descrim();
1124     scroll_world(dx*scale, dy*scale, is_scrolling);
1127 /**
1128  * Scroll canvas by specific coordinate amount.
1129  */
1130 void
1131 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1133     g_assert(_widget);
1135     Geom::Rect const viewbox = canvas->getViewbox();
1137     sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1139     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1140     sp_box3d_context_update_lines(event_context);
1142     _widget->updateRulers();
1143     _widget->updateScrollbars(_d2w.descrim());
1146 bool
1147 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1149     gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
1151     // autoscrolldistance is in screen pixels, but the display area is in document units
1152     autoscrolldistance /= _d2w.descrim();
1153     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1154     Geom::Rect dbox = get_display_area();
1155     dbox.expandBy(-autoscrolldistance);
1157     if (!(p[NR::X] > dbox.min()[NR::X] && p[NR::X] < dbox.max()[NR::X]) ||
1158         !(p[NR::Y] > dbox.min()[NR::Y] && p[NR::Y] < dbox.max()[NR::Y])   ) {
1160         Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1162         gdouble x_to;
1163         if (p[NR::X] < dbox.min()[NR::X])
1164             x_to = dbox.min()[NR::X];
1165         else if (p[NR::X] > dbox.max()[NR::X])
1166             x_to = dbox.max()[NR::X];
1167         else
1168             x_to = p[NR::X];
1170         gdouble y_to;
1171         if (p[NR::Y] < dbox.min()[NR::Y])
1172             y_to = dbox.min()[NR::Y];
1173         else if (p[NR::Y] > dbox.max()[NR::Y])
1174             y_to = dbox.max()[NR::Y];
1175         else
1176             y_to = p[NR::Y];
1178         Geom::Point const d_dt(x_to, y_to);
1179         Geom::Point const d_w( d_dt * _d2w );
1180         Geom::Point const moved_w( d_w - s_w );
1182         if (autoscrollspeed == 0)
1183             autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
1185         if (autoscrollspeed != 0)
1186             scroll_world (autoscrollspeed * moved_w);
1188         return true;
1189     }
1190     return false;
1193 bool
1194 SPDesktop::is_iconified()
1196     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1199 void
1200 SPDesktop::iconify()
1202     _widget->setIconified();
1205 bool
1206 SPDesktop::is_maximized()
1208     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1211 void
1212 SPDesktop::maximize()
1214     _widget->setMaximized();
1217 bool
1218 SPDesktop::is_fullscreen()
1220     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1223 void
1224 SPDesktop::fullscreen()
1226     _widget->setFullscreen();
1229 /** \brief  Checks to see if the user is working in focused mode
1231         Returns the value of \c _focusMode
1232 */
1233 bool
1234 SPDesktop::is_focusMode()
1236         return _focusMode;
1239 /** \brief  Changes whether the user is in focus mode or not
1240         \param  mode  Which mode the view should be in
1242 */
1243 void
1244 SPDesktop::focusMode (bool mode)
1246         if (mode == _focusMode) { return; }
1248         _focusMode = mode;
1250         layoutWidget();
1251         //sp_desktop_widget_layout(SPDesktopWidget);
1253         return;
1256 void
1257 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1259     _widget->getGeometry (x, y, w, h);
1262 void
1263 SPDesktop::setWindowPosition (Geom::Point p)
1265     _widget->setPosition (p);
1268 void
1269 SPDesktop::setWindowSize (gint w, gint h)
1271     _widget->setSize (w, h);
1274 void
1275 SPDesktop::setWindowTransient (void *p, int transient_policy)
1277     _widget->setTransient (p, transient_policy);
1280 Gtk::Window*
1281 SPDesktop::getToplevel( )
1283     return _widget->getWindow();
1286 void
1287 SPDesktop::presentWindow()
1289     _widget->present();
1292 bool
1293 SPDesktop::warnDialog (gchar *text)
1295     return _widget->warnDialog (text);
1298 void
1299 SPDesktop::toggleRulers()
1301     _widget->toggleRulers();
1304 void
1305 SPDesktop::toggleScrollbars()
1307     _widget->toggleScrollbars();
1310 void
1311 SPDesktop::layoutWidget()
1313     _widget->layout();
1316 void
1317 SPDesktop::destroyWidget()
1319     _widget->destroy();
1322 bool
1323 SPDesktop::shutdown()
1325     return _widget->shutdown();
1328 bool SPDesktop::onDeleteUI (GdkEventAny*)
1330     if(shutdown()) 
1331         return true;
1333     destroyWidget();
1334     return false;
1337 /**
1338  *  onWindowStateEvent
1339  *
1340  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1341  *  Since GTK doesn't have a way to query this state information directly, we
1342  *  record it for the desktop here, and also possibly trigger a layout.
1343  */
1344 bool
1345 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1347         // Record the desktop window's state
1348     window_state = event->new_window_state;
1350     // Layout may differ depending on full-screen mode or not
1351     GdkWindowState changed = event->changed_mask;
1352     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1353         layoutWidget();
1354     }
1356         return false;
1359 void
1360 SPDesktop::setToolboxFocusTo (gchar const *label)
1362     _widget->setToolboxFocusTo (label);
1365 void
1366 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1368     _widget->setToolboxAdjustmentValue (id, val);
1371 void
1372 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1374     _widget->setToolboxSelectOneValue (id, val);
1377 bool
1378 SPDesktop::isToolboxButtonActive (gchar const *id)
1380     return _widget->isToolboxButtonActive (id);
1383 void
1384 SPDesktop::emitToolSubselectionChanged(gpointer data)
1386         _tool_subselection_changed.emit(data);
1387         inkscape_subselection_changed (this);
1390 void
1391 SPDesktop::updateNow()
1393   sp_canvas_update_now(canvas);
1396 void
1397 SPDesktop::enableInteraction()
1399   _widget->enableInteraction();
1402 void SPDesktop::disableInteraction()
1404   _widget->disableInteraction();
1407 void SPDesktop::setWaitingCursor()
1409     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1410     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1411     gdk_cursor_unref(waiting);
1412     // GDK needs the flush for the cursor change to take effect
1413     gdk_flush();
1414     waiting_cursor = true;
1417 void SPDesktop::clearWaitingCursor()
1419   if (waiting_cursor)
1420       sp_event_context_update_cursor(sp_desktop_event_context(this));
1423 void SPDesktop::toggleColorProfAdjust()
1425     _widget->toggleColorProfAdjust();
1428 void SPDesktop::toggleGrids()
1430     if (namedview->grids) {
1431         if(gridgroup) {
1432             showGrids(!grids_visible);
1433         }
1434     } else {
1435         //there is no grid present at the moment. add a rectangular grid and make it visible
1436         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1437         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1438         showGrids(true);
1439     }
1442 void SPDesktop::showGrids(bool show, bool dirty_document)
1444     grids_visible = show;
1445     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1446     if (show) {
1447         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1448     } else {
1449         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1450     }
1453 void SPDesktop::toggleSnapping()
1455     bool v = namedview->snap_manager.getSnapEnabledGlobally();
1456     Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1457     sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1460 //----------------------------------------------------------------------
1461 // Callback implementations. The virtual ones are connected by the view.
1463 void
1464 SPDesktop::onPositionSet (double x, double y)
1466     _widget->viewSetPosition (Geom::Point(x,y));
1469 void
1470 SPDesktop::onResized (double /*x*/, double /*y*/)
1472    // Nothing called here
1475 /**
1476  * Redraw callback; queues Gtk redraw; connected by View.
1477  */
1478 void
1479 SPDesktop::onRedrawRequested ()
1481     if (main) {
1482         _widget->requestCanvasUpdate();
1483     }
1486 void
1487 SPDesktop::updateCanvasNow()
1489   _widget->requestCanvasUpdateAndWait();
1492 /**
1493  * Associate document with desktop.
1494  */
1495 void
1496 SPDesktop::setDocument (SPDocument *doc)
1498     if (this->doc() && doc) {
1499         namedview->hide(this);
1500         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1501     }
1503     if (_layer_hierarchy) {
1504         _layer_hierarchy->clear();
1505         delete _layer_hierarchy;
1506     }
1507     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1508     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1509     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1510     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1511     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1513     /* setup EventLog */
1514     event_log = new Inkscape::EventLog(doc);
1515     doc->addUndoObserver(*event_log);
1517     _commit_connection.disconnect();
1518     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1520     /// \todo fixme: This condition exists to make sure the code
1521     /// inside is NOT called on initialization, only on replacement. But there
1522     /// are surely more safe methods to accomplish this.
1523     // TODO since the comment had reversed logic, check the intent of this block of code:
1524     if (drawing) {
1525         NRArenaItem *ai = 0;
1527         namedview = sp_document_namedview (doc, NULL);
1528         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1529         number = namedview->getViewCount();
1531         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1532                 SP_CANVAS_ARENA (drawing)->arena,
1533                 dkey,
1534                 SP_ITEM_SHOW_DISPLAY);
1535         if (ai) {
1536             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1537         }
1538         namedview->show(this);
1539         /* Ugly hack */
1540         activate_guides (true);
1541         /* Ugly hack */
1542         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1543     }
1545     _document_replaced_signal.emit (this, doc);
1547     View::setDocument (doc);
1550 void
1551 SPDesktop::onStatusMessage
1552 (Inkscape::MessageType type, gchar const *message)
1554     if (_widget) {
1555         _widget->setMessage(type, message);
1556     }
1559 void
1560 SPDesktop::onDocumentURISet (gchar const* uri)
1562     _widget->setTitle(uri);
1565 /**
1566  * Resized callback.
1567  */
1568 void
1569 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1571     _doc2dt[5] = height;
1572     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1573     Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1574     SP_CTRLRECT(page)->setRectangle(a);
1575     SP_CTRLRECT(page_border)->setRectangle(a);
1579 void
1580 SPDesktop::_onActivate (SPDesktop* dt)
1582     if (!dt->_widget) return;
1583     dt->_widget->activateDesktop();
1586 void
1587 SPDesktop::_onDeactivate (SPDesktop* dt)
1589     if (!dt->_widget) return;
1590     dt->_widget->deactivateDesktop();
1593 void
1594 SPDesktop::_onSelectionModified
1595 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1597     if (!dt->_widget) return;
1598     dt->_widget->updateScrollbars (dt->_d2w.descrim());
1601 static void
1602 _onSelectionChanged
1603 (Inkscape::Selection *selection, SPDesktop *desktop)
1605     /** \todo
1606      * only change the layer for single selections, or what?
1607      * This seems reasonable -- for multiple selections there can be many
1608      * different layers involved.
1609      */
1610     SPItem *item=selection->singleItem();
1611     if (item) {
1612         SPObject *layer=desktop->layerForObject(item);
1613         if ( layer && layer != desktop->currentLayer() ) {
1614             desktop->setCurrentLayer(layer);
1615         }
1616     }
1619 /**
1620  * Calls event handler of current event context.
1621  * \param arena Unused
1622  * \todo fixme
1623  */
1624 static gint
1625 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1627     if (ai) {
1628         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1629         return sp_event_context_item_handler (desktop->event_context, spi, event);
1630     } else {
1631         return sp_event_context_root_handler (desktop->event_context, event);
1632     }
1635 static void
1636 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1637     g_return_if_fail(SP_IS_GROUP(layer));
1638     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1641 /// Callback
1642 static void
1643 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1644     g_return_if_fail(SP_IS_GROUP(layer));
1645     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1648 /// Callback
1649 static void
1650 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1651                                          SPDesktop *desktop)
1653     desktop->_layer_changed_signal.emit (bottom);
1656 /// Called when document is starting to be rebuilt.
1657 static void
1658 _reconstruction_start (SPDesktop * desktop)
1660     // printf("Desktop, starting reconstruction\n");
1661     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1662     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1664     /*
1665     GSList const * selection_objs = desktop->selection->list();
1666     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1668     }
1669     */
1670     desktop->selection->clear();
1672     // printf("Desktop, starting reconstruction end\n");
1675 /// Called when document rebuild is finished.
1676 static void
1677 _reconstruction_finish (SPDesktop * desktop)
1679     // printf("Desktop, finishing reconstruction\n");
1680     if (desktop->_reconstruction_old_layer_id == NULL)
1681         return;
1683     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1684     if (newLayer != NULL)
1685         desktop->setCurrentLayer(newLayer);
1687     g_free(desktop->_reconstruction_old_layer_id);
1688     desktop->_reconstruction_old_layer_id = NULL;
1689     // printf("Desktop, finishing reconstruction end\n");
1690     return;
1693 /**
1694  * Namedview_modified callback.
1695  */
1696 static void
1697 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1699     SPNamedView *nv=SP_NAMEDVIEW(obj);
1701     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1703         /* Recalculate snap distances */
1704         /* FIXME: why is the desktop getting involved in setting up something
1705         ** that is entirely to do with the namedview?
1706         */
1707         _update_snap_distances (desktop);
1709         /* Show/hide page background */
1710         if (nv->pagecolor & 0xff) {
1711             sp_canvas_item_show (desktop->table);
1712             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1713             sp_canvas_item_move_to_z (desktop->table, 0);
1714         } else {
1715             sp_canvas_item_hide (desktop->table);
1716         }
1718         /* Show/hide page border */
1719         if (nv->showborder) {
1720             // show
1721             sp_canvas_item_show (desktop->page_border);
1722             // set color and shadow
1723             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1724             if (nv->pageshadow) {
1725                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1726             }
1727             // place in the z-order stack
1728             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1729                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1730             } else {
1731                 int order = sp_canvas_item_order (desktop->page_border);
1732                 int morder = sp_canvas_item_order (desktop->drawing);
1733                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1734                                     morder - order);
1735             }
1736         } else {
1737                 sp_canvas_item_hide (desktop->page_border);
1738                 if (nv->pageshadow) {
1739                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1740                 }
1741         }
1743         /* Show/hide page shadow */
1744         if (nv->showpageshadow && nv->pageshadow) {
1745             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1746         } else {
1747             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1748         }
1750         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1751             (SP_RGBA32_R_U(nv->pagecolor) +
1752              SP_RGBA32_G_U(nv->pagecolor) +
1753              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1754             // the background color is light or transparent, use black outline
1755             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1756         } else { // use white outline
1757             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1758         }
1759     }
1762 /**
1763  * Callback to reset snapper's distances.
1764  */
1765 static void
1766 _update_snap_distances (SPDesktop *desktop)
1768     SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1770     SPNamedView &nv = *desktop->namedview;
1772     //tell all grid snappers
1773     for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1774         Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1775         grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
1776                                                                       *nv.gridtoleranceunit,
1777                                                                       px));
1778     }
1780     nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
1781                                                                        *nv.guidetoleranceunit,
1782                                                                        px));
1783     nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
1784                                                                         *nv.objecttoleranceunit,
1785                                                                         px));
1789 Geom::Matrix SPDesktop::w2d() const
1791     return _w2d;
1794 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1796     return p * _w2d;
1799 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1801     return p * _d2w;
1804 Geom::Matrix SPDesktop::doc2dt() const
1806     return _doc2dt;
1809 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1811     return p * _doc2dt;
1814 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1816     return p * _doc2dt.inverse();
1820 /**
1821  * Pop event context from desktop's context stack. Never used.
1822  */
1823 // void
1824 // SPDesktop::pop_event_context (unsigned int key)
1825 // {
1826 //    SPEventContext *ec = NULL;
1827 //
1828 //    if (event_context && event_context->key == key) {
1829 //        g_return_if_fail (event_context);
1830 //        g_return_if_fail (event_context->next);
1831 //        ec = event_context;
1832 //        sp_event_context_deactivate (ec);
1833 //        event_context = ec->next;
1834 //        sp_event_context_activate (event_context);
1835 //        _event_context_changed_signal.emit (this, ec);
1836 //    }
1837 //
1838 //    SPEventContext *ref = event_context;
1839 //    while (ref && ref->next && ref->next->key != key)
1840 //        ref = ref->next;
1841 //
1842 //    if (ref && ref->next) {
1843 //        ec = ref->next;
1844 //        ref->next = ec->next;
1845 //    }
1846 //
1847 //    if (ec) {
1848 //        sp_event_context_finish (ec);
1849 //        g_object_unref (G_OBJECT (ec));
1850 //    }
1851 // }
1853 /*
1854   Local Variables:
1855   mode:c++
1856   c-file-style:"stroustrup"
1857   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1858   indent-tabs-mode:nil
1859   fill-column:99
1860   End:
1861 */
1862 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :