Code

Patch by Adib for 238796
[inkscape.git] / src / desktop.cpp
1 #define __SP_DESKTOP_C__
3 /** \file
4  * Editable view implementation
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   MenTaLguY <mental@rydia.net>
9  *   bulia byak <buliabyak@users.sf.net>
10  *   Ralf Stephan <ralf@ark.in-berlin.de>
11  *   John Bintz <jcoswell@coswellproductions.org>
12  *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
13  *
14  * Copyright (C) 2007 Jon A. Cruz
15  * Copyright (C) 2006-2008 Johan Engelen
16  * Copyright (C) 2006 John Bintz
17  * Copyright (C) 2004 MenTaLguY
18  * Copyright (C) 1999-2002 Lauris Kaplinski
19  * Copyright (C) 2000-2001 Ximian, Inc.
20  *
21  * Released under GNU GPL, read the file 'COPYING' for more information
22  */
24 /** \class SPDesktop
25  * SPDesktop is a subclass of View, implementing an editable document
26  * canvas.  It is extensively used by many UI controls that need certain
27  * visual representations of their own.
28  *
29  * SPDesktop provides a certain set of SPCanvasItems, serving as GUI
30  * layers of different control objects. The one containing the whole
31  * document is the drawing layer. In addition to it, there are grid,
32  * guide, sketch and control layers. The sketch layer is used for
33  * temporary drawing objects, before the real objects in document are
34  * created. The control layer contains editing knots, rubberband and
35  * similar non-document UI objects.
36  *
37  * Each SPDesktop is associated with a SPNamedView node of the document
38  * tree.  Currently, all desktops are created from a single main named
39  * view, but in the future there may be support for different ones.
40  * SPNamedView serves as an in-document container for desktop-related
41  * data, like grid and guideline placement, snapping options and so on.
42  *
43  * Associated with each SPDesktop are the two most important editing
44  * related objects - SPSelection and SPEventContext.
45  *
46  * Sodipodi keeps track of the active desktop and invokes notification
47  * signals whenever it changes. UI elements can use these to update their
48  * display to the selection of the currently active editing window.
49  * (Lauris Kaplinski)
50  */
52 #ifdef HAVE_CONFIG_H
53 # include "config.h"
54 #endif
56 #include <glibmm/i18n.h>
57 #include <sigc++/functors/mem_fun.h>
58 #include <gtkmm.h>
60 #include <2geom/rect.h>
61 #include "macros.h"
62 #include "inkscape-private.h"
63 #include "desktop.h"
64 #include "desktop-events.h"
65 #include "desktop-handles.h"
66 #include "document.h"
67 #include "message-stack.h"
68 #include "selection.h"
69 #include "select-context.h"
70 #include "sp-namedview.h"
71 #include "color.h"
72 #include "sp-item-group.h"
73 #include "preferences.h"
74 #include "object-hierarchy.h"
75 #include "helper/units.h"
76 #include "display/canvas-arena.h"
77 #include "display/nr-arena.h"
78 #include "display/gnome-canvas-acetate.h"
79 #include "display/sodipodi-ctrlrect.h"
80 #include "display/sp-canvas-util.h"
81 #include "display/canvas-temporary-item-list.h"
82 #include "display/snap-indicator.h"
83 #include "ui/dialog/dialog-manager.h"
84 #include "xml/repr.h"
85 #include "message-context.h"
86 #include "device-manager.h"
87 #include "layer-fns.h"
88 #include "layer-manager.h"
89 #include "event-log.h"
90 #include "display/canvas-grid.h"
91 #include "widgets/desktop-widget.h"
92 #include "box3d-context.h"
94 #include "display/sp-canvas.h"
96 namespace Inkscape { namespace XML { class Node; }}
98 // Callback declarations
99 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
100 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
101 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
102 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
103 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
104 static void _reconstruction_start(SPDesktop * desktop);
105 static void _reconstruction_finish(SPDesktop * desktop);
106 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
108 /**
109  * Return new desktop object.
110  * \pre namedview != NULL.
111  * \pre canvas != NULL.
112  */
113 SPDesktop::SPDesktop() :
114     _dlg_mgr( 0 ),
115     namedview( 0 ),
116     canvas( 0 ),
117     selection( 0 ),
118     event_context( 0 ),
119     layer_manager( 0 ),
120     event_log( 0 ),
121     temporary_item_list( 0 ),
122     snapindicator( 0 ),
123     acetate( 0 ),
124     main( 0 ),
125     gridgroup( 0 ),
126     guides( 0 ),
127     drawing( 0 ),
128     sketch( 0 ),
129     controls( 0 ),
130     tempgroup ( 0 ),
131     table( 0 ),
132     page( 0 ),
133     page_border( 0 ),
134     current( 0 ),
135     _focusMode(false),
136     zooms_past( 0 ),
137     zooms_future( 0 ),
138     dkey( 0 ),
139     number( 0 ),
140     window_state(0),
141     interaction_disabled_counter( 0 ),
142     waiting_cursor( false ),
143     guides_active( false ),
144     gr_item( 0 ),
145     gr_point_type( 0 ),
146     gr_point_i( 0 ),
147     gr_fill_or_stroke( true ),
148     _layer_hierarchy( 0 ),
149     _reconstruction_old_layer_id( 0 ),
150     _display_mode(Inkscape::RENDERMODE_NORMAL),
151     _widget( 0 ),
152     _inkscape( 0 ),
153     _guides_message_context( 0 ),
154     _active( false ),
155     _w2d(),
156     _d2w(),
157     _doc2dt( Geom::Scale(1, -1) ),
158     grids_visible( false )
160     _d2w.setIdentity();
161     _w2d.setIdentity();
163     selection = Inkscape::GC::release( new Inkscape::Selection(this) );
166 void
167 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
169     // Temporary workaround for link order issues:
170     Inkscape::DeviceManager::getManager().getDevices();
171     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
173     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
175     current = prefs->getStyle("/desktop/style");
177     namedview = nv;
178     canvas = aCanvas;
180     SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
181     /* Kill flicker */
182     sp_document_ensure_up_to_date (document);
184     /* Setup Dialog Manager */
185     _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
187     dkey = sp_item_display_key_new (1);
189     /* Connect document */
190     setDocument (document);
192     number = namedview->getViewCount();
195     /* Setup Canvas */
196     g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
198     SPCanvasGroup *root = sp_canvas_root (canvas);
200     /* Setup adminstrative layers */
201     acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
202     g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
203     main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
204     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
206     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
207     SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
208     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
209     sp_canvas_item_move_to_z (table, 0);
211     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
212     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
213     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
215     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
216     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
218     SP_CANVAS_ARENA (drawing)->arena->delta = prefs->getDouble("/options/cursortolerance/value", 1.0); // default is 1 px
220     if (prefs->getBool("/options/startmode/outline")) {
221         // Start in outline mode
222         setDisplayModeOutline();
223     } else {
224         // Start in normal mode, default
225         setDisplayModeNormal();
226     }
228     // The order in which these canvas items are added determines the z-order. It's therefore
229     // important to add the tempgroup (which will contain the snapindicator) before adding the
230     // controls. Only this way one will be able to quickly (before the snap indicator has
231     // disappeared) reselect a node after snapping it. If the z-order is wrong however, this
232     // will not work (the snap indicator is on top of the node handler; is the snapindicator
233     // being selected? or does it intercept some of the events that should have gone to the
234     // node handler? see bug https://bugs.launchpad.net/inkscape/+bug/414142)
235     gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
236     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
237     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
238     tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
239     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
241     /* Push select tool to the bottom of stack */
242     /** \todo
243      * FIXME: this is the only call to this.  Everything else seems to just
244      * call "set" instead of "push".  Can we assume that there is only one
245      * context ever?
246      */
247     push_event_context (SP_TYPE_SELECT_CONTEXT, "/tools/select", SP_EVENT_CONTEXT_STATIC);
249     // display rect and zoom are now handled in sp_desktop_widget_realize()
251     Geom::Rect const d(Geom::Point(0.0, 0.0),
252                        Geom::Point(sp_document_width(document), sp_document_height(document)));
254     SP_CTRLRECT(page)->setRectangle(d);
255     SP_CTRLRECT(page_border)->setRectangle(d);
257     /* the following sets the page shadow on the canvas
258        It was originally set to 5, which is really cheesy!
259        It now is an attribute in the document's namedview. If a value of
260        0 is used, then the constructor for a shadow is not initialized.
261     */
263     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
264         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
265     }
268     /* Connect event for page resize */
269     _doc2dt[5] = sp_document_height (document);
270     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
272     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
274     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
275             SP_CANVAS_ARENA (drawing)->arena,
276             dkey,
277             SP_ITEM_SHOW_DISPLAY);
278     if (ai) {
279         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
280     }
282     namedview->show(this);
283     /* Ugly hack */
284     activate_guides (true);
285     /* Ugly hack */
286     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
288 /* Set up notification of rebuilding the document, this allows
289        for saving object related settings in the document. */
290     _reconstruction_start_connection =
291         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
292     _reconstruction_finish_connection =
293         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
294     _reconstruction_old_layer_id = NULL;
296     // ?
297     // sp_active_desktop_set (desktop);
298     _inkscape = INKSCAPE;
300     _activate_connection = _activate_signal.connect(
301         sigc::bind(
302             sigc::ptr_fun(_onActivate),
303             this
304         )
305     );
306      _deactivate_connection = _deactivate_signal.connect(
307         sigc::bind(
308             sigc::ptr_fun(_onDeactivate),
309             this
310         )
311     );
313     _sel_modified_connection = selection->connectModified(
314         sigc::bind(
315             sigc::ptr_fun(&_onSelectionModified),
316             this
317         )
318     );
319     _sel_changed_connection = selection->connectChanged(
320         sigc::bind(
321             sigc::ptr_fun(&_onSelectionChanged),
322             this
323         )
324     );
327     /* setup LayerManager */
328     //   (Setting up after the connections are all in place, as it may use some of them)
329     layer_manager = new Inkscape::LayerManager( this );
331     showGrids(namedview->grids_visible, false);
333     temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
334     snapindicator = new Inkscape::Display::SnapIndicator ( this );
338 void SPDesktop::destroy()
340     if (snapindicator) {
341         delete snapindicator;
342         snapindicator = NULL;
343     }
344     if (temporary_item_list) {
345         delete temporary_item_list;
346         temporary_item_list = NULL;
347     }
349     if (selection) {
350         delete selection;
351         selection = NULL;
352     }
354     namedview->hide(this);
356     _activate_connection.disconnect();
357     _deactivate_connection.disconnect();
358     _sel_modified_connection.disconnect();
359     _sel_changed_connection.disconnect();
360     _modified_connection.disconnect();
361     _commit_connection.disconnect();
362     _reconstruction_start_connection.disconnect();
363     _reconstruction_finish_connection.disconnect();
365     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
366     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
367     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
369     while (event_context) {
370         SPEventContext *ec = event_context;
371         event_context = ec->next;
372         sp_event_context_finish (ec);
373         g_object_unref (G_OBJECT (ec));
374     }
376     if (_layer_hierarchy) {
377         delete _layer_hierarchy;
378 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
379     }
381     if (layer_manager) {
382         delete layer_manager;
383         layer_manager = NULL;
384     }
386     if (_inkscape) {
387         _inkscape = NULL;
388     }
390     if (drawing) {
391         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
392         drawing = NULL;
393     }
395     delete _guides_message_context;
396     _guides_message_context = NULL;
398     g_list_free (zooms_past);
399     g_list_free (zooms_future);
402 SPDesktop::~SPDesktop() {}
404 //--------------------------------------------------------------------
405 /* Public methods */
408 /* These methods help for temporarily showing things on-canvas.
409  * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
410  * is when you want to prematurely remove the item from the canvas, by calling
411  * desktop->remove_temporary_canvasitem(tempitem).
412  */
413 /** Note that lifetime is measured in milliseconds
414  * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
415  * delete the object for you and the reference will become invalid without you knowing it.
416  * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
417  * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
418  * because the object might be deleted already without you knowing it.
419  * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
420  */
421 Inkscape::Display::TemporaryItem *
422 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
424     if (move_to_bottom) {
425         sp_canvas_item_move_to_z(item, 0);
426     }
428     return temporary_item_list->add_item(item, lifetime);
431 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
432 */
433 void
434 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
436     // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
437     if (tempitem && temporary_item_list) {
438         temporary_item_list->delete_item(tempitem);
439     }
442 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
443     SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
444     canvas->rendermode = mode;
445     _display_mode = mode;
446     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
447     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
450 void SPDesktop::displayModeToggle() {
451     switch (_display_mode) {
452     case Inkscape::RENDERMODE_NORMAL:
453         _setDisplayMode(Inkscape::RENDERMODE_NO_FILTERS);
454         break;
455     case Inkscape::RENDERMODE_NO_FILTERS:
456         _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
457         break;
458     case Inkscape::RENDERMODE_OUTLINE:
459     default:
460         _setDisplayMode(Inkscape::RENDERMODE_NORMAL);
461     }
464 /**
465  * Returns current root (=bottom) layer.
466  */
467 SPObject *SPDesktop::currentRoot() const
469     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
472 /**
473  * Returns current top layer.
474  */
475 SPObject *SPDesktop::currentLayer() const
477     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
480 /**
481  * Sets the current layer of the desktop.
482  *
483  * Make \a object the top layer.
484  */
485 void SPDesktop::setCurrentLayer(SPObject *object) {
486     g_return_if_fail(SP_IS_GROUP(object));
487     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
488     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
489     _layer_hierarchy->setBottom(object);
492 void SPDesktop::toggleLayerSolo(SPObject *object) {
493     g_return_if_fail(SP_IS_GROUP(object));
494     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
496     bool othersShowing = false;
497     std::vector<SPObject*> layers;
498     for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
499         layers.push_back(obj);
500         othersShowing |= !SP_ITEM(obj)->isHidden();
501     }
502     for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
503         layers.push_back(obj);
504         othersShowing |= !SP_ITEM(obj)->isHidden();
505     }
508     if ( SP_ITEM(object)->isHidden() ) {
509         SP_ITEM(object)->setHidden(false);
510     }
512     for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
513         SP_ITEM(*it)->setHidden(othersShowing);
514     }
517 /**
518  * Return layer that contains \a object.
519  */
520 SPObject *SPDesktop::layerForObject(SPObject *object) {
521     g_return_val_if_fail(object != NULL, NULL);
523     SPObject *root=currentRoot();
524     object = SP_OBJECT_PARENT(object);
525     while ( object && object != root && !isLayer(object) ) {
526         object = SP_OBJECT_PARENT(object);
527     }
528     return object;
531 /**
532  * True if object is a layer.
533  */
534 bool SPDesktop::isLayer(SPObject *object) const {
535     return ( SP_IS_GROUP(object)
536              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
537                   == SPGroup::LAYER ) );
540 /**
541  * True if desktop viewport fully contains \a item's bbox.
542  */
543 bool SPDesktop::isWithinViewport (SPItem *item) const
545     Geom::Rect const viewport = get_display_area();
546     Geom::OptRect const bbox = sp_item_bbox_desktop(item);
547     if (bbox) {
548         return viewport.contains(*bbox);
549     } else {
550         return true;
551     }
554 ///
555 bool SPDesktop::itemIsHidden(SPItem const *item) const {
556     return item->isHidden(this->dkey);
559 /**
560  * Set activate property of desktop; emit signal if changed.
561  */
562 void
563 SPDesktop::set_active (bool new_active)
565     if (new_active != _active) {
566         _active = new_active;
567         if (new_active) {
568             _activate_signal.emit();
569         } else {
570             _deactivate_signal.emit();
571         }
572     }
575 /**
576  * Set activate status of current desktop's named view.
577  */
578 void
579 SPDesktop::activate_guides(bool activate)
581     guides_active = activate;
582     namedview->activateGuides(this, activate);
585 /**
586  * Make desktop switch documents.
587  */
588 void
589 SPDesktop::change_document (SPDocument *theDocument)
591     g_return_if_fail (theDocument != NULL);
593     /* unselect everything before switching documents */
594     selection->clear();
596     setDocument (theDocument);
598     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
599        (this can probably be done in a better way) */
600     Gtk::Window *parent = this->getToplevel();
601     g_assert(parent != NULL);
602     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
603     if (dtw) dtw->desktop = this;
604     sp_desktop_widget_update_namedview(dtw);
606     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
607     _document_replaced_signal.emit (this, theDocument);
610 /**
611  * Make desktop switch event contexts.
612  */
613 void
614 SPDesktop::set_event_context (GtkType type, const gchar *config)
616     SPEventContext *ec;
617     while (event_context) {
618         ec = event_context;
619         sp_event_context_deactivate (ec);
620         event_context = ec->next;
621         sp_event_context_finish (ec);
622         g_object_unref (G_OBJECT (ec));
623     }
625     ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
626     ec->next = event_context;
627     event_context = ec;
628     sp_event_context_activate (ec);
629     _event_context_changed_signal.emit (this, ec);
632 /**
633  * Push event context onto desktop's context stack.
634  */
635 void
636 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
638     SPEventContext *ref, *ec;
640     if (event_context && event_context->key == key) return;
641     ref = event_context;
642     while (ref && ref->next && ref->next->key != key) ref = ref->next;
643     if (ref && ref->next) {
644         ec = ref->next;
645         ref->next = ec->next;
646         sp_event_context_finish (ec);
647         g_object_unref (G_OBJECT (ec));
648     }
650     if (event_context) sp_event_context_deactivate (event_context);
651     ec = sp_event_context_new (type, this, config, key);
652     ec->next = event_context;
653     event_context = ec;
654     sp_event_context_activate (ec);
655     _event_context_changed_signal.emit (this, ec);
658 /**
659  * Sets the coordinate status to a given point
660  */
661 void
662 SPDesktop::set_coordinate_status (Geom::Point p) {
663     _widget->setCoordinateStatus(p);
666 /**
667  * \see sp_document_item_from_list_at_point_bottom()
668  */
669 SPItem *
670 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
672     g_return_val_if_fail (doc() != NULL, NULL);
673     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
676 /**
677  * \see sp_document_item_at_point()
678  */
679 SPItem *
680 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
682     g_return_val_if_fail (doc() != NULL, NULL);
683     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
686 /**
687  * \see sp_document_group_at_point()
688  */
689 SPItem *
690 SPDesktop::group_at_point (Geom::Point const p) const
692     g_return_val_if_fail (doc() != NULL, NULL);
693     return sp_document_group_at_point (doc(), dkey, p);
696 /**
697  * \brief  Returns the mouse point in document coordinates; if mouse is
698  * outside the canvas, returns the center of canvas viewpoint
699  */
700 Geom::Point
701 SPDesktop::point() const
703     Geom::Point p = _widget->getPointer();
704     Geom::Point pw = sp_canvas_window_to_world (canvas, p);
705     p = w2d(pw);
707     Geom::Rect const r = canvas->getViewbox();
709     Geom::Point r0 = w2d(r.min());
710     Geom::Point r1 = w2d(r.max());
712     if (p[Geom::X] >= r0[Geom::X] &&
713         p[Geom::X] <= r1[Geom::X] &&
714         p[Geom::Y] >= r1[Geom::Y] &&
715         p[Geom::Y] <= r0[Geom::Y])
716     {
717         return p;
718     } else {
719         return (r0 + r1) / 2;
720     }
723 /**
724  * Put current zoom data in history list.
725  */
726 void
727 SPDesktop::push_current_zoom (GList **history)
729     Geom::Rect const area = get_display_area();
731     NRRect *old_zoom = g_new(NRRect, 1);
732     old_zoom->x0 = area.min()[Geom::X];
733     old_zoom->x1 = area.max()[Geom::X];
734     old_zoom->y0 = area.min()[Geom::Y];
735     old_zoom->y1 = area.max()[Geom::Y];
736     if ( *history == NULL
737          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
738                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
739                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
740                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
741     {
742         *history = g_list_prepend (*history, old_zoom);
743     }
746 /**
747  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
748  */
749 void
750 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
752     g_assert(_widget);
754     // save the zoom
755     if (log) {
756         push_current_zoom(&zooms_past);
757         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
758         g_list_free (zooms_future);
759         zooms_future = NULL;
760     }
762     double const cx = 0.5 * (x0 + x1);
763     double const cy = 0.5 * (y0 + y1);
765     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
766     Geom::Rect viewbox = canvas->getViewbox();
767     viewbox.expandBy(-border);
769     double scale = _d2w.descrim();
770     double newscale;
771     if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
772         newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
773     } else {
774         newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
775     }
777     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
779     int clear = FALSE;
780     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
781         /* Set zoom factors */
782         _d2w = Geom::Scale(newscale, -newscale);
783         _w2d = Geom::Scale(1/newscale, 1/-newscale);
784         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
785         clear = TRUE;
786     }
788     /* Calculate top left corner (in document pixels) */
789     x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
790     y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
792     /* Scroll */
793     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
795     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
796     sp_box3d_context_update_lines(event_context);
798     _widget->updateRulers();
799     _widget->updateScrollbars(_d2w.descrim());
800     _widget->updateZoom();
803 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
805     set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
808 /**
809  * Return viewbox dimensions.
810  */
811 Geom::Rect SPDesktop::get_display_area() const
813     Geom::Rect const viewbox = canvas->getViewbox();
815     double const scale = _d2w[0];
817     return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
818                       Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
821 /**
822  * Revert back to previous zoom if possible.
823  */
824 void
825 SPDesktop::prev_zoom()
827     if (zooms_past == NULL) {
828         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
829         return;
830     }
832     // push current zoom into forward zooms list
833     push_current_zoom (&zooms_future);
835     // restore previous zoom
836     set_display_area (((NRRect *) zooms_past->data)->x0,
837             ((NRRect *) zooms_past->data)->y0,
838             ((NRRect *) zooms_past->data)->x1,
839             ((NRRect *) zooms_past->data)->y1,
840             0, false);
842     // remove the just-added zoom from the past zooms list
843     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
846 /**
847  * Set zoom to next in list.
848  */
849 void
850 SPDesktop::next_zoom()
852     if (zooms_future == NULL) {
853         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
854         return;
855     }
857     // push current zoom into past zooms list
858     push_current_zoom (&zooms_past);
860     // restore next zoom
861     set_display_area (((NRRect *) zooms_future->data)->x0,
862             ((NRRect *) zooms_future->data)->y0,
863             ((NRRect *) zooms_future->data)->x1,
864             ((NRRect *) zooms_future->data)->y1,
865             0, false);
867     // remove the just-used zoom from the zooms_future list
868     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
871 #include "tools-switch.h"
872 #include "node-context.h"
873 #include "shape-editor.h"
874 #include "nodepath.h"
876 /** \brief  Performs a quick zoom into what the user is working on
877     \param  enable  Whether we're going in or out of quick zoom
879 */
880 void
881 SPDesktop::zoom_quick (bool enable)
883     if (enable == _quick_zoom_enabled) {
884         return;
885     }
887     if (enable == true) {
888         _quick_zoom_stored_area = get_display_area();
889         bool zoomed = false;
891         if (!zoomed) {
892             SPItem * singleItem = selection->singleItem();
893             if (singleItem != NULL && tools_isactive(this, TOOLS_NODES)) {
895                 Inkscape::NodePath::Path * nodepath = event_context->shape_editor->get_nodepath();
896                 // printf("I've got a nodepath, crazy\n");
898                                 if (nodepath) {
899                                         Geom::Rect nodes;
900                                         bool firstnode = true;
902                                         if (nodepath->selected) {
903                                                 for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
904                                                    Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
905                                                         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
906                                                            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
907                                                                 if (node->selected) {
908                                                                         // printf("\tSelected node\n");
909                                                                         if (firstnode) {
910                                                                                 nodes = Geom::Rect(node->pos, node->pos);
911                                                                                 firstnode = false;
912                                                                         } else {
913                                                                                 nodes.expandTo(node->pos);
914                                                                         }
916                                                                         if (node->p.other != NULL) {
917                                                                                 /* Include previous node pos */
918                                                                                 nodes.expandTo(node->p.other->pos);
920                                                                                 /* Include previous handle */
921                                                                                 if (!sp_node_side_is_line(node, &node->p)) {
922                                                                                         nodes.expandTo(node->p.pos);
923                                                                                 }
924                                                                         }
926                                                                         if (node->n.other != NULL) {
927                                                                                 /* Include previous node pos */
928                                                                                 nodes.expandTo(node->n.other->pos);
930                                                                                 /* Include previous handle */
931                                                                                 if (!sp_node_side_is_line(node, &node->n)) {
932                                                                                         nodes.expandTo(node->n.pos);
933                                                                                 }
934                                                                         }
935                                                                 }
936                                                         }
937                                                 }
939                                                 if (!firstnode && nodes.area() * 2.0 < _quick_zoom_stored_area.area()) {
940                                                         set_display_area(nodes, 10);
941                                                         zoomed = true;
942                                                 }
943                                         }
944                                 }
945             }
946         }
948         if (!zoomed) {
949             Geom::OptRect const d = selection->bounds();
950             if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
951                 set_display_area(*d, 10);
952                 zoomed = true;
953             }
954         }
956         if (!zoomed) {
957             zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
958             zoomed = true;
959         }
960     } else {
961         set_display_area(_quick_zoom_stored_area, 0);
962     }
964     _quick_zoom_enabled = enable;
965     return;
968 /**
969  * Zoom to point with absolute zoom factor.
970  */
971 void
972 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
974     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
976     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
977     // this check prevents "sliding" when trying to zoom in at maximum zoom;
978     /// \todo someone please fix calculations properly and remove this hack
979     if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
980         return;
982     Geom::Rect const viewbox = canvas->getViewbox();
984     double const width2 = viewbox.dimensions()[Geom::X] / zoom;
985     double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
987     set_display_area(cx - px * width2,
988                      cy - py * height2,
989                      cx + (1 - px) * width2,
990                      cy + (1 - py) * height2,
991                      0.0);
994 /**
995  * Zoom to center with absolute zoom factor.
996  */
997 void
998 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
1000     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
1003 /**
1004  * Zoom to point with relative zoom factor.
1005  */
1006 void
1007 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
1009     Geom::Rect const area = get_display_area();
1011     if (cx < area.min()[Geom::X]) {
1012         cx = area.min()[Geom::X];
1013     }
1014     if (cx > area.max()[Geom::X]) {
1015         cx = area.max()[Geom::X];
1016     }
1017     if (cy < area.min()[Geom::Y]) {
1018         cy = area.min()[Geom::Y];
1019     }
1020     if (cy > area.max()[Geom::Y]) {
1021         cy = area.max()[Geom::Y];
1022     }
1024     gdouble const scale = _d2w.descrim() * zoom;
1025     double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
1026     double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
1028     zoom_absolute_keep_point(cx, cy, px, py, scale);
1031 /**
1032  * Zoom to center with relative zoom factor.
1033  */
1034 void
1035 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1037     gdouble scale = _d2w.descrim() * zoom;
1038     zoom_absolute (cx, cy, scale);
1041 /**
1042  * Set display area to origin and current document dimensions.
1043  */
1044 void
1045 SPDesktop::zoom_page()
1047     Geom::Rect d(Geom::Point(0, 0),
1048                  Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1050     if (d.minExtent() < 1.0) {
1051         return;
1052     }
1054     set_display_area(d, 10);
1057 /**
1058  * Set display area to current document width.
1059  */
1060 void
1061 SPDesktop::zoom_page_width()
1063     Geom::Rect const a = get_display_area();
1065     if (sp_document_width(doc()) < 1.0) {
1066         return;
1067     }
1069     Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1070                  Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1072     set_display_area(d, 10);
1075 /**
1076  * Zoom to selection.
1077  */
1078 void
1079 SPDesktop::zoom_selection()
1081     Geom::OptRect const d = selection->bounds();
1083     if ( !d || d->minExtent() < 0.1 ) {
1084         return;
1085     }
1087     set_display_area(*d, 10);
1090 /**
1091  * Tell widget to let zoom widget grab keyboard focus.
1092  */
1093 void
1094 SPDesktop::zoom_grab_focus()
1096     _widget->letZoomGrabFocus();
1099 /**
1100  * Zoom to whole drawing.
1101  */
1102 void
1103 SPDesktop::zoom_drawing()
1105     g_return_if_fail (doc() != NULL);
1106     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1107     g_return_if_fail (docitem != NULL);
1109     Geom::OptRect d = sp_item_bbox_desktop(docitem);
1111     /* Note that the second condition here indicates that
1112     ** there are no items in the drawing.
1113     */
1114     if ( !d || d->minExtent() < 0.1 ) {
1115         return;
1116     }
1118     set_display_area(*d, 10);
1121 /**
1122  * Scroll canvas by specific coordinate amount in svg coordinates.
1123  */
1124 void
1125 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1127     double scale = _d2w.descrim();
1128     scroll_world(dx*scale, dy*scale, is_scrolling);
1131 /**
1132  * Scroll canvas by specific coordinate amount.
1133  */
1134 void
1135 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1137     g_assert(_widget);
1139     Geom::Rect const viewbox = canvas->getViewbox();
1141     sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1143     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1144     sp_box3d_context_update_lines(event_context);
1146     _widget->updateRulers();
1147     _widget->updateScrollbars(_d2w.descrim());
1150 bool
1151 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1153     using Geom::X;
1154     using Geom::Y;
1156     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1157     gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1159     // autoscrolldistance is in screen pixels, but the display area is in document units
1160     autoscrolldistance /= _d2w.descrim();
1161     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1162     Geom::Rect dbox = get_display_area();
1163     dbox.expandBy(-autoscrolldistance);
1165     if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1166         !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y])   ) {
1168         Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1170         gdouble x_to;
1171         if (p[X] < dbox.min()[X])
1172             x_to = dbox.min()[X];
1173         else if (p[X] > dbox.max()[X])
1174             x_to = dbox.max()[X];
1175         else
1176             x_to = p[X];
1178         gdouble y_to;
1179         if (p[Y] < dbox.min()[Y])
1180             y_to = dbox.min()[Y];
1181         else if (p[Y] > dbox.max()[Y])
1182             y_to = dbox.max()[Y];
1183         else
1184             y_to = p[Y];
1186         Geom::Point const d_dt(x_to, y_to);
1187         Geom::Point const d_w( d_dt * _d2w );
1188         Geom::Point const moved_w( d_w - s_w );
1190         if (autoscrollspeed == 0)
1191             autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1193         if (autoscrollspeed != 0)
1194             scroll_world (autoscrollspeed * moved_w);
1196         return true;
1197     }
1198     return false;
1201 bool
1202 SPDesktop::is_iconified()
1204     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1207 void
1208 SPDesktop::iconify()
1210     _widget->setIconified();
1213 bool
1214 SPDesktop::is_maximized()
1216     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1219 void
1220 SPDesktop::maximize()
1222     _widget->setMaximized();
1225 bool
1226 SPDesktop::is_fullscreen()
1228     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1231 void
1232 SPDesktop::fullscreen()
1234     _widget->setFullscreen();
1237 /** \brief  Checks to see if the user is working in focused mode
1239     Returns the value of \c _focusMode
1240 */
1241 bool
1242 SPDesktop::is_focusMode()
1244     return _focusMode;
1247 /** \brief  Changes whether the user is in focus mode or not
1248     \param  mode  Which mode the view should be in
1250 */
1251 void
1252 SPDesktop::focusMode (bool mode)
1254     if (mode == _focusMode) { return; }
1256     _focusMode = mode;
1258     layoutWidget();
1259     //sp_desktop_widget_layout(SPDesktopWidget);
1261     return;
1264 void
1265 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1267     _widget->getGeometry (x, y, w, h);
1270 void
1271 SPDesktop::setWindowPosition (Geom::Point p)
1273     _widget->setPosition (p);
1276 void
1277 SPDesktop::setWindowSize (gint w, gint h)
1279     _widget->setSize (w, h);
1282 void
1283 SPDesktop::setWindowTransient (void *p, int transient_policy)
1285     _widget->setTransient (p, transient_policy);
1288 Gtk::Window*
1289 SPDesktop::getToplevel( )
1291     return _widget->getWindow();
1294 void
1295 SPDesktop::presentWindow()
1297     _widget->present();
1300 bool
1301 SPDesktop::warnDialog (gchar *text)
1303     return _widget->warnDialog (text);
1306 void
1307 SPDesktop::toggleRulers()
1309     _widget->toggleRulers();
1312 void
1313 SPDesktop::toggleScrollbars()
1315     _widget->toggleScrollbars();
1318 void
1319 SPDesktop::layoutWidget()
1321     _widget->layout();
1324 void
1325 SPDesktop::destroyWidget()
1327     _widget->destroy();
1330 bool
1331 SPDesktop::shutdown()
1333     return _widget->shutdown();
1336 bool SPDesktop::onDeleteUI (GdkEventAny*)
1338     if(shutdown())
1339         return true;
1341     destroyWidget();
1342     return false;
1345 /**
1346  *  onWindowStateEvent
1347  *
1348  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1349  *  Since GTK doesn't have a way to query this state information directly, we
1350  *  record it for the desktop here, and also possibly trigger a layout.
1351  */
1352 bool
1353 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1355     // Record the desktop window's state
1356     window_state = event->new_window_state;
1358     // Layout may differ depending on full-screen mode or not
1359     GdkWindowState changed = event->changed_mask;
1360     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1361         layoutWidget();
1362     }
1364     return false;
1367 void
1368 SPDesktop::setToolboxFocusTo (gchar const *label)
1370     _widget->setToolboxFocusTo (label);
1373 void
1374 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1376     _widget->setToolboxAdjustmentValue (id, val);
1379 void
1380 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1382     _widget->setToolboxSelectOneValue (id, val);
1385 bool
1386 SPDesktop::isToolboxButtonActive (gchar const *id)
1388     return _widget->isToolboxButtonActive (id);
1391 void
1392 SPDesktop::emitToolSubselectionChanged(gpointer data)
1394     _tool_subselection_changed.emit(data);
1395     inkscape_subselection_changed (this);
1398 void
1399 SPDesktop::updateNow()
1401   sp_canvas_update_now(canvas);
1404 void
1405 SPDesktop::enableInteraction()
1407   _widget->enableInteraction();
1410 void SPDesktop::disableInteraction()
1412   _widget->disableInteraction();
1415 void SPDesktop::setWaitingCursor()
1417     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1418     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1419     gdk_cursor_unref(waiting);
1420     // GDK needs the flush for the cursor change to take effect
1421     gdk_flush();
1422     waiting_cursor = true;
1425 void SPDesktop::clearWaitingCursor()
1427   if (waiting_cursor)
1428       sp_event_context_update_cursor(sp_desktop_event_context(this));
1431 void SPDesktop::toggleColorProfAdjust()
1433     _widget->toggleColorProfAdjust();
1436 void SPDesktop::toggleGrids()
1438     if (namedview->grids) {
1439         if(gridgroup) {
1440             showGrids(!grids_visible);
1441         }
1442     } else {
1443         //there is no grid present at the moment. add a rectangular grid and make it visible
1444         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1445         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1446         showGrids(true);
1447     }
1450 void SPDesktop::showGrids(bool show, bool dirty_document)
1452     grids_visible = show;
1453     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1454     if (show) {
1455         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1456     } else {
1457         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1458     }
1461 void SPDesktop::toggleSnapGlobal()
1463     bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1464     Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1465     sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1468 //----------------------------------------------------------------------
1469 // Callback implementations. The virtual ones are connected by the view.
1471 void
1472 SPDesktop::onPositionSet (double x, double y)
1474     _widget->viewSetPosition (Geom::Point(x,y));
1477 void
1478 SPDesktop::onResized (double /*x*/, double /*y*/)
1480    // Nothing called here
1483 /**
1484  * Redraw callback; queues Gtk redraw; connected by View.
1485  */
1486 void
1487 SPDesktop::onRedrawRequested ()
1489     if (main) {
1490         _widget->requestCanvasUpdate();
1491     }
1494 void
1495 SPDesktop::updateCanvasNow()
1497   _widget->requestCanvasUpdateAndWait();
1500 /**
1501  * Associate document with desktop.
1502  */
1503 void
1504 SPDesktop::setDocument (SPDocument *doc)
1506     if (this->doc() && doc) {
1507         namedview->hide(this);
1508         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1509     }
1511     if (_layer_hierarchy) {
1512         _layer_hierarchy->clear();
1513         delete _layer_hierarchy;
1514     }
1515     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1516     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1517     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1518     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1519     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1521     /* setup EventLog */
1522     event_log = new Inkscape::EventLog(doc);
1523     doc->addUndoObserver(*event_log);
1525     _commit_connection.disconnect();
1526     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1528     /// \todo fixme: This condition exists to make sure the code
1529     /// inside is NOT called on initialization, only on replacement. But there
1530     /// are surely more safe methods to accomplish this.
1531     // TODO since the comment had reversed logic, check the intent of this block of code:
1532     if (drawing) {
1533         NRArenaItem *ai = 0;
1535         namedview = sp_document_namedview (doc, NULL);
1536         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1537         number = namedview->getViewCount();
1539         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1540                 SP_CANVAS_ARENA (drawing)->arena,
1541                 dkey,
1542                 SP_ITEM_SHOW_DISPLAY);
1543         if (ai) {
1544             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1545         }
1546         namedview->show(this);
1547         /* Ugly hack */
1548         activate_guides (true);
1549         /* Ugly hack */
1550         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1551     }
1553     _document_replaced_signal.emit (this, doc);
1555     View::setDocument (doc);
1558 void
1559 SPDesktop::onStatusMessage
1560 (Inkscape::MessageType type, gchar const *message)
1562     if (_widget) {
1563         _widget->setMessage(type, message);
1564     }
1567 void
1568 SPDesktop::onDocumentURISet (gchar const* uri)
1570     _widget->setTitle(uri);
1573 /**
1574  * Resized callback.
1575  */
1576 void
1577 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1579     _doc2dt[5] = height;
1580     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1581     Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1582     SP_CTRLRECT(page)->setRectangle(a);
1583     SP_CTRLRECT(page_border)->setRectangle(a);
1587 void
1588 SPDesktop::_onActivate (SPDesktop* dt)
1590     if (!dt->_widget) return;
1591     dt->_widget->activateDesktop();
1594 void
1595 SPDesktop::_onDeactivate (SPDesktop* dt)
1597     if (!dt->_widget) return;
1598     dt->_widget->deactivateDesktop();
1601 void
1602 SPDesktop::_onSelectionModified
1603 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1605     if (!dt->_widget) return;
1606     dt->_widget->updateScrollbars (dt->_d2w.descrim());
1609 static void
1610 _onSelectionChanged
1611 (Inkscape::Selection *selection, SPDesktop *desktop)
1613     /** \todo
1614      * only change the layer for single selections, or what?
1615      * This seems reasonable -- for multiple selections there can be many
1616      * different layers involved.
1617      */
1618     SPItem *item=selection->singleItem();
1619     if (item) {
1620         SPObject *layer=desktop->layerForObject(item);
1621         if ( layer && layer != desktop->currentLayer() ) {
1622             desktop->setCurrentLayer(layer);
1623         }
1624     }
1627 /**
1628  * Calls event handler of current event context.
1629  * \param arena Unused
1630  * \todo fixme
1631  */
1632 static gint
1633 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1635     if (ai) {
1636         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1637         return sp_event_context_item_handler (desktop->event_context, spi, event);
1638     } else {
1639         return sp_event_context_root_handler (desktop->event_context, event);
1640     }
1643 static void
1644 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1645     g_return_if_fail(SP_IS_GROUP(layer));
1646     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1649 /// Callback
1650 static void
1651 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1652     g_return_if_fail(SP_IS_GROUP(layer));
1653     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1656 /// Callback
1657 static void
1658 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1659                                          SPDesktop *desktop)
1661     desktop->_layer_changed_signal.emit (bottom);
1664 /// Called when document is starting to be rebuilt.
1665 static void
1666 _reconstruction_start (SPDesktop * desktop)
1668     // printf("Desktop, starting reconstruction\n");
1669     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1670     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1672     /*
1673     GSList const * selection_objs = desktop->selection->list();
1674     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1676     }
1677     */
1678     desktop->selection->clear();
1680     // printf("Desktop, starting reconstruction end\n");
1683 /// Called when document rebuild is finished.
1684 static void
1685 _reconstruction_finish (SPDesktop * desktop)
1687     // printf("Desktop, finishing reconstruction\n");
1688     if (desktop->_reconstruction_old_layer_id == NULL)
1689         return;
1691     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1692     if (newLayer != NULL)
1693         desktop->setCurrentLayer(newLayer);
1695     g_free(desktop->_reconstruction_old_layer_id);
1696     desktop->_reconstruction_old_layer_id = NULL;
1697     // printf("Desktop, finishing reconstruction end\n");
1698     return;
1701 /**
1702  * Namedview_modified callback.
1703  */
1704 static void
1705 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1707     SPNamedView *nv=SP_NAMEDVIEW(obj);
1709     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1711         /* Show/hide page background */
1712         if (nv->pagecolor & 0xff) {
1713             sp_canvas_item_show (desktop->table);
1714             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1715             sp_canvas_item_move_to_z (desktop->table, 0);
1716         } else {
1717             sp_canvas_item_hide (desktop->table);
1718         }
1720         /* Show/hide page border */
1721         if (nv->showborder) {
1722             // show
1723             sp_canvas_item_show (desktop->page_border);
1724             // set color and shadow
1725             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1726             if (nv->pageshadow) {
1727                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1728             }
1729             // place in the z-order stack
1730             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1731                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1732             } else {
1733                 int order = sp_canvas_item_order (desktop->page_border);
1734                 int morder = sp_canvas_item_order (desktop->drawing);
1735                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1736                     morder - order);
1737             }
1738         } else {
1739                 sp_canvas_item_hide (desktop->page_border);
1740                 if (nv->pageshadow) {
1741                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1742                 }
1743         }
1745         /* Show/hide page shadow */
1746         if (nv->showpageshadow && nv->pageshadow) {
1747             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1748         } else {
1749             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1750         }
1752         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1753         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1754             (SP_RGBA32_R_U(nv->pagecolor) +
1755              SP_RGBA32_G_U(nv->pagecolor) +
1756              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1757             // the background color is light or transparent, use black outline
1758             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1759         } else { // use white outline
1760             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1761         }
1762     }
1765 Geom::Matrix SPDesktop::w2d() const
1767     return _w2d;
1770 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1772     return p * _w2d;
1775 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1777     return p * _d2w;
1780 Geom::Matrix SPDesktop::doc2dt() const
1782     return _doc2dt;
1785 Geom::Matrix SPDesktop::dt2doc() const
1787     // doc2dt is its own inverse
1788     return _doc2dt;
1791 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1793     return p * _doc2dt;
1796 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1798     return p * dt2doc();
1802 /**
1803  * Pop event context from desktop's context stack. Never used.
1804  */
1805 // void
1806 // SPDesktop::pop_event_context (unsigned int key)
1807 // {
1808 //    SPEventContext *ec = NULL;
1809 //
1810 //    if (event_context && event_context->key == key) {
1811 //        g_return_if_fail (event_context);
1812 //        g_return_if_fail (event_context->next);
1813 //        ec = event_context;
1814 //        sp_event_context_deactivate (ec);
1815 //        event_context = ec->next;
1816 //        sp_event_context_activate (event_context);
1817 //        _event_context_changed_signal.emit (this, ec);
1818 //    }
1819 //
1820 //    SPEventContext *ref = event_context;
1821 //    while (ref && ref->next && ref->next->key != key)
1822 //        ref = ref->next;
1823 //
1824 //    if (ref && ref->next) {
1825 //        ec = ref->next;
1826 //        ref->next = ec->next;
1827 //    }
1828 //
1829 //    if (ec) {
1830 //        sp_event_context_finish (ec);
1831 //        g_object_unref (G_OBJECT (ec));
1832 //    }
1833 // }
1835 /*
1836   Local Variables:
1837   mode:c++
1838   c-file-style:"stroustrup"
1839   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1840   indent-tabs-mode:nil
1841   fill-column:99
1842   End:
1843 */
1844 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :