Code

A simple layout document as to what, why and how is cppification.
[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"
93 #include "desktop-style.h"
95 // TODO those includes are only for node tool quick zoom. Remove them after fixing it.
96 #include "ui/tool/node-tool.h"
97 #include "ui/tool/control-point-selection.h"
99 #include "display/sp-canvas.h"
101 namespace Inkscape { namespace XML { class Node; }}
103 // Callback declarations
104 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
105 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
106 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
107 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
108 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
109 static void _reconstruction_start(SPDesktop * desktop);
110 static void _reconstruction_finish(SPDesktop * desktop);
111 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
113 /**
114  * Return new desktop object.
115  * \pre namedview != NULL.
116  * \pre canvas != NULL.
117  */
118 SPDesktop::SPDesktop() :
119     _dlg_mgr( 0 ),
120     namedview( 0 ),
121     canvas( 0 ),
122     selection( 0 ),
123     event_context( 0 ),
124     layer_manager( 0 ),
125     event_log( 0 ),
126     temporary_item_list( 0 ),
127     snapindicator( 0 ),
128     acetate( 0 ),
129     main( 0 ),
130     gridgroup( 0 ),
131     guides( 0 ),
132     drawing( 0 ),
133     sketch( 0 ),
134     controls( 0 ),
135     tempgroup ( 0 ),
136     table( 0 ),
137     page( 0 ),
138     page_border( 0 ),
139     current( 0 ),
140     _focusMode(false),
141     zooms_past( 0 ),
142     zooms_future( 0 ),
143     dkey( 0 ),
144     number( 0 ),
145     window_state(0),
146     interaction_disabled_counter( 0 ),
147     waiting_cursor( false ),
148     guides_active( false ),
149     gr_item( 0 ),
150     gr_point_type( 0 ),
151     gr_point_i( 0 ),
152     gr_fill_or_stroke( true ),
153     _layer_hierarchy( 0 ),
154     _reconstruction_old_layer_id( 0 ),
155     _display_mode(Inkscape::RENDERMODE_NORMAL),
156     _widget( 0 ),
157     _inkscape( 0 ),
158     _guides_message_context( 0 ),
159     _active( false ),
160     _w2d(),
161     _d2w(),
162     _doc2dt( Geom::Scale(1, -1) ),
163     grids_visible( false )
165     _d2w.setIdentity();
166     _w2d.setIdentity();
168     selection = Inkscape::GC::release( new Inkscape::Selection(this) );
171 void
172 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas, Inkscape::UI::View::EditWidgetInterface *widget)
174     _widget = widget;
176     // Temporary workaround for link order issues:
177     Inkscape::DeviceManager::getManager().getDevices();
178     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
180     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
182     current = prefs->getStyle("/desktop/style");
184     namedview = nv;
185     canvas = aCanvas;
187     SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
188     /* Kill flicker */
189     document->ensure_up_to_date ();
191     /* Setup Dialog Manager */
192     _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
194     dkey = SPItem::display_key_new (1);
196     /* Connect document */
197     setDocument (document);
199     number = namedview->getViewCount();
202     /* Setup Canvas */
203     g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
205     SPCanvasGroup *root = sp_canvas_root (canvas);
207     /* Setup adminstrative layers */
208     acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
209     g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
210     main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
211     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
213     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
214     SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
215     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
216     sp_canvas_item_move_to_z (table, 0);
218     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
219     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
220     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
222     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
223     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
225     SP_CANVAS_ARENA (drawing)->arena->delta = prefs->getDouble("/options/cursortolerance/value", 1.0); // default is 1 px
227     if (prefs->getBool("/options/startmode/outline")) {
228         // Start in outline mode
229         setDisplayModeOutline();
230     } else {
231         // Start in normal mode, default
232         setDisplayModeNormal();
233     }
235     // The order in which these canvas items are added determines the z-order. It's therefore
236     // important to add the tempgroup (which will contain the snapindicator) before adding the
237     // controls. Only this way one will be able to quickly (before the snap indicator has
238     // disappeared) reselect a node after snapping it. If the z-order is wrong however, this
239     // will not work (the snap indicator is on top of the node handler; is the snapindicator
240     // being selected? or does it intercept some of the events that should have gone to the
241     // node handler? see bug https://bugs.launchpad.net/inkscape/+bug/414142)
242     gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
243     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
244     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
245     tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
246     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
248     /* Push select tool to the bottom of stack */
249     /** \todo
250      * FIXME: this is the only call to this.  Everything else seems to just
251      * call "set" instead of "push".  Can we assume that there is only one
252      * context ever?
253      */
254     push_event_context (SP_TYPE_SELECT_CONTEXT, "/tools/select", SP_EVENT_CONTEXT_STATIC);
256     // display rect and zoom are now handled in sp_desktop_widget_realize()
258     Geom::Rect const d(Geom::Point(0.0, 0.0),
259                        Geom::Point(document->getWidth(), document->getHeight()));
261     SP_CTRLRECT(page)->setRectangle(d);
262     SP_CTRLRECT(page_border)->setRectangle(d);
264     /* the following sets the page shadow on the canvas
265        It was originally set to 5, which is really cheesy!
266        It now is an attribute in the document's namedview. If a value of
267        0 is used, then the constructor for a shadow is not initialized.
268     */
270     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
271         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
272     }
275     /* Connect event for page resize */
276     _doc2dt[5] = document->getHeight ();
277     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
279     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
281     NRArenaItem *ai = SP_ITEM (sp_document_root (document))->invoke_show (
282             SP_CANVAS_ARENA (drawing)->arena,
283             dkey,
284             SP_ITEM_SHOW_DISPLAY);
285     if (ai) {
286         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
287     }
289     namedview->show(this);
290     /* Ugly hack */
291     activate_guides (true);
292     /* Ugly hack */
293     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
295 /* Set up notification of rebuilding the document, this allows
296        for saving object related settings in the document. */
297     _reconstruction_start_connection =
298         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
299     _reconstruction_finish_connection =
300         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
301     _reconstruction_old_layer_id = NULL;
303     // ?
304     // sp_active_desktop_set (desktop);
305     _inkscape = INKSCAPE;
307     _activate_connection = _activate_signal.connect(
308         sigc::bind(
309             sigc::ptr_fun(_onActivate),
310             this
311         )
312     );
313      _deactivate_connection = _deactivate_signal.connect(
314         sigc::bind(
315             sigc::ptr_fun(_onDeactivate),
316             this
317         )
318     );
320     _sel_modified_connection = selection->connectModified(
321         sigc::bind(
322             sigc::ptr_fun(&_onSelectionModified),
323             this
324         )
325     );
326     _sel_changed_connection = selection->connectChanged(
327         sigc::bind(
328             sigc::ptr_fun(&_onSelectionChanged),
329             this
330         )
331     );
334     /* setup LayerManager */
335     //   (Setting up after the connections are all in place, as it may use some of them)
336     layer_manager = new Inkscape::LayerManager( this );
338     showGrids(namedview->grids_visible, false);
340     temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
341     snapindicator = new Inkscape::Display::SnapIndicator ( this );
345 void SPDesktop::destroy()
347     if (snapindicator) {
348         delete snapindicator;
349         snapindicator = NULL;
350     }
351     if (temporary_item_list) {
352         delete temporary_item_list;
353         temporary_item_list = NULL;
354     }
356     if (selection) {
357         delete selection;
358         selection = NULL;
359     }
361     namedview->hide(this);
363     _activate_connection.disconnect();
364     _deactivate_connection.disconnect();
365     _sel_modified_connection.disconnect();
366     _sel_changed_connection.disconnect();
367     _modified_connection.disconnect();
368     _commit_connection.disconnect();
369     _reconstruction_start_connection.disconnect();
370     _reconstruction_finish_connection.disconnect();
372     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
373     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
374     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
376     while (event_context) {
377         SPEventContext *ec = event_context;
378         event_context = ec->next;
379         sp_event_context_finish (ec);
380         g_object_unref (G_OBJECT (ec));
381     }
383     if (_layer_hierarchy) {
384         delete _layer_hierarchy;
385 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
386     }
388     if (layer_manager) {
389         delete layer_manager;
390         layer_manager = NULL;
391     }
393     if (_inkscape) {
394         _inkscape = NULL;
395     }
397     if (drawing) {
398         SP_ITEM (sp_document_root (doc()))->invoke_hide (dkey);
399         drawing = NULL;
400     }
402     delete _guides_message_context;
403     _guides_message_context = NULL;
405     g_list_free (zooms_past);
406     g_list_free (zooms_future);
409 SPDesktop::~SPDesktop() {}
411 //--------------------------------------------------------------------
412 /* Public methods */
415 /* These methods help for temporarily showing things on-canvas.
416  * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
417  * is when you want to prematurely remove the item from the canvas, by calling
418  * desktop->remove_temporary_canvasitem(tempitem).
419  */
420 /** Note that lifetime is measured in milliseconds
421  * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
422  * delete the object for you and the reference will become invalid without you knowing it.
423  * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
424  * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
425  * because the object might be deleted already without you knowing it.
426  * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
427  */
428 Inkscape::Display::TemporaryItem *
429 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
431     if (move_to_bottom) {
432         sp_canvas_item_move_to_z(item, 0);
433     }
435     return temporary_item_list->add_item(item, lifetime);
438 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
439 */
440 void
441 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
443     // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
444     if (tempitem && temporary_item_list) {
445         temporary_item_list->delete_item(tempitem);
446     }
449 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
450     SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
451     canvas->rendermode = mode;
452     _display_mode = mode;
453     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
454     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
457 void SPDesktop::displayModeToggle() {
458     switch (_display_mode) {
459     case Inkscape::RENDERMODE_NORMAL:
460         _setDisplayMode(Inkscape::RENDERMODE_NO_FILTERS);
461         break;
462     case Inkscape::RENDERMODE_NO_FILTERS:
463         _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
464         break;
465     case Inkscape::RENDERMODE_OUTLINE:
466         _setDisplayMode(Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW);
467         break;
468     case Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW:
469     default:
470         _setDisplayMode(Inkscape::RENDERMODE_NORMAL);
471     }
474 /**
475  * Returns current root (=bottom) layer.
476  */
477 SPObject *SPDesktop::currentRoot() const
479     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
482 /**
483  * Returns current top layer.
484  */
485 SPObject *SPDesktop::currentLayer() const
487     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
490 /**
491  * Sets the current layer of the desktop.
492  *
493  * Make \a object the top layer.
494  */
495 void SPDesktop::setCurrentLayer(SPObject *object) {
496     g_return_if_fail(SP_IS_GROUP(object));
497     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
498     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
499     _layer_hierarchy->setBottom(object);
502 void SPDesktop::toggleLayerSolo(SPObject *object) {
503     g_return_if_fail(SP_IS_GROUP(object));
504     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
506     bool othersShowing = false;
507     std::vector<SPObject*> layers;
508     for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
509         layers.push_back(obj);
510         othersShowing |= !SP_ITEM(obj)->isHidden();
511     }
512     for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
513         layers.push_back(obj);
514         othersShowing |= !SP_ITEM(obj)->isHidden();
515     }
518     if ( SP_ITEM(object)->isHidden() ) {
519         SP_ITEM(object)->setHidden(false);
520     }
522     for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
523         SP_ITEM(*it)->setHidden(othersShowing);
524     }
527 /**
528  * Return layer that contains \a object.
529  */
530 SPObject *SPDesktop::layerForObject(SPObject *object) {
531     g_return_val_if_fail(object != NULL, NULL);
533     SPObject *root=currentRoot();
534     object = SP_OBJECT_PARENT(object);
535     while ( object && object != root && !isLayer(object) ) {
536         object = SP_OBJECT_PARENT(object);
537     }
538     return object;
541 /**
542  * True if object is a layer.
543  */
544 bool SPDesktop::isLayer(SPObject *object) const {
545     return ( SP_IS_GROUP(object)
546              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
547                   == SPGroup::LAYER ) );
550 /**
551  * True if desktop viewport fully contains \a item's bbox.
552  */
553 bool SPDesktop::isWithinViewport (SPItem *item) const
555     Geom::Rect const viewport = get_display_area();
556     Geom::OptRect const bbox = item->getBboxDesktop();
557     if (bbox) {
558         return viewport.contains(*bbox);
559     } else {
560         return true;
561     }
564 ///
565 bool SPDesktop::itemIsHidden(SPItem const *item) const {
566     return item->isHidden(this->dkey);
569 /**
570  * Set activate property of desktop; emit signal if changed.
571  */
572 void
573 SPDesktop::set_active (bool new_active)
575     if (new_active != _active) {
576         _active = new_active;
577         if (new_active) {
578             _activate_signal.emit();
579         } else {
580             _deactivate_signal.emit();
581         }
582     }
585 /**
586  * Set activate status of current desktop's named view.
587  */
588 void
589 SPDesktop::activate_guides(bool activate)
591     guides_active = activate;
592     namedview->activateGuides(this, activate);
595 /**
596  * Make desktop switch documents.
597  */
598 void
599 SPDesktop::change_document (SPDocument *theDocument)
601     g_return_if_fail (theDocument != NULL);
603     /* unselect everything before switching documents */
604     selection->clear();
606     setDocument (theDocument);
608     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
609        (this can probably be done in a better way) */
610     Gtk::Window *parent = this->getToplevel();
611     g_assert(parent != NULL);
612     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
613     if (dtw) {
614         dtw->desktop = this;
615     }
616     dtw->updateNamedview();
618     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
619     _document_replaced_signal.emit (this, theDocument);
622 /**
623  * Make desktop switch event contexts.
624  */
625 void
626 SPDesktop::set_event_context (GtkType type, const gchar *config)
628     SPEventContext *ec;
629     while (event_context) {
630         ec = event_context;
631         sp_event_context_deactivate (ec);
632         // we have to keep event_context valid during destruction - otherwise writing
633         // destructors is next to impossible
634         SPEventContext *next = ec->next;
635         sp_event_context_finish (ec);
636         g_object_unref (G_OBJECT (ec));
637         event_context = next;
638     }
640     ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
641     ec->next = event_context;
642     event_context = ec;
643     sp_event_context_activate (ec);
644     _event_context_changed_signal.emit (this, ec);
647 /**
648  * Push event context onto desktop's context stack.
649  */
650 void
651 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
653     SPEventContext *ref, *ec;
655     if (event_context && event_context->key == key) return;
656     ref = event_context;
657     while (ref && ref->next && ref->next->key != key) ref = ref->next;
658     if (ref && ref->next) {
659         ec = ref->next;
660         ref->next = ec->next;
661         sp_event_context_finish (ec);
662         g_object_unref (G_OBJECT (ec));
663     }
665     if (event_context) sp_event_context_deactivate (event_context);
666     ec = sp_event_context_new (type, this, config, key);
667     ec->next = event_context;
668     event_context = ec;
669     sp_event_context_activate (ec);
670     _event_context_changed_signal.emit (this, ec);
673 /**
674  * Sets the coordinate status to a given point
675  */
676 void
677 SPDesktop::set_coordinate_status (Geom::Point p) {
678     _widget->setCoordinateStatus(p);
681 /**
682  * \see sp_document_item_from_list_at_point_bottom()
683  */
684 SPItem *
685 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
687     g_return_val_if_fail (doc() != NULL, NULL);
688     return SPDocument::item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
691 /**
692  * \see sp_document_item_at_point()
693  */
694 SPItem *
695 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
697     g_return_val_if_fail (doc() != NULL, NULL);
698     return doc()->item_at_point ( dkey, p, into_groups, upto);
701 /**
702  * \see sp_document_group_at_point()
703  */
704 SPItem *
705 SPDesktop::group_at_point (Geom::Point const p) const
707     g_return_val_if_fail (doc() != NULL, NULL);
708     return doc()->group_at_point (dkey, p);
711 /**
712  * \brief  Returns the mouse point in document coordinates; if mouse is
713  * outside the canvas, returns the center of canvas viewpoint
714  */
715 Geom::Point
716 SPDesktop::point() const
718     Geom::Point p = _widget->getPointer();
719     Geom::Point pw = sp_canvas_window_to_world (canvas, p);
720     p = w2d(pw);
722     Geom::Rect const r = canvas->getViewbox();
724     Geom::Point r0 = w2d(r.min());
725     Geom::Point r1 = w2d(r.max());
727     if (p[Geom::X] >= r0[Geom::X] &&
728         p[Geom::X] <= r1[Geom::X] &&
729         p[Geom::Y] >= r1[Geom::Y] &&
730         p[Geom::Y] <= r0[Geom::Y])
731     {
732         return p;
733     } else {
734         return (r0 + r1) / 2;
735     }
738 /**
739  * Put current zoom data in history list.
740  */
741 void
742 SPDesktop::push_current_zoom (GList **history)
744     Geom::Rect const area = get_display_area();
746     NRRect *old_zoom = g_new(NRRect, 1);
747     old_zoom->x0 = area.min()[Geom::X];
748     old_zoom->x1 = area.max()[Geom::X];
749     old_zoom->y0 = area.min()[Geom::Y];
750     old_zoom->y1 = area.max()[Geom::Y];
751     if ( *history == NULL
752          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
753                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
754                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
755                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
756     {
757         *history = g_list_prepend (*history, old_zoom);
758     }
761 /**
762  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
763  */
764 void
765 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
767     g_assert(_widget);
769     // save the zoom
770     if (log) {
771         push_current_zoom(&zooms_past);
772         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
773         g_list_free (zooms_future);
774         zooms_future = NULL;
775     }
777     double const cx = 0.5 * (x0 + x1);
778     double const cy = 0.5 * (y0 + y1);
780     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
781     Geom::Rect viewbox = canvas->getViewbox();
782     viewbox.expandBy(-border);
784     double scale = _d2w.descrim();
785     double newscale;
786     if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
787         newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
788     } else {
789         newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
790     }
792     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
794     int clear = FALSE;
795     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
796         // zoom changed - set new zoom factors
797         _d2w = Geom::Scale(newscale, -newscale);
798         _w2d = Geom::Scale(1/newscale, 1/-newscale);
799         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
800         clear = TRUE;
801         signal_zoom_changed.emit(_d2w.descrim());
802     }
804     /* Calculate top left corner (in document pixels) */
805     x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
806     y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
808     /* Scroll */
809     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
811     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
812     sp_box3d_context_update_lines(event_context);
814     _widget->updateRulers();
815     _widget->updateScrollbars(_d2w.descrim());
816     _widget->updateZoom();
819 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
821     set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
824 /**
825  * Return viewbox dimensions.
826  */
827 Geom::Rect SPDesktop::get_display_area() const
829     Geom::Rect const viewbox = canvas->getViewbox();
831     double const scale = _d2w[0];
833     return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
834                       Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
837 /**
838  * Revert back to previous zoom if possible.
839  */
840 void
841 SPDesktop::prev_zoom()
843     if (zooms_past == NULL) {
844         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
845         return;
846     }
848     // push current zoom into forward zooms list
849     push_current_zoom (&zooms_future);
851     // restore previous zoom
852     set_display_area (((NRRect *) zooms_past->data)->x0,
853             ((NRRect *) zooms_past->data)->y0,
854             ((NRRect *) zooms_past->data)->x1,
855             ((NRRect *) zooms_past->data)->y1,
856             0, false);
858     // remove the just-added zoom from the past zooms list
859     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
862 /**
863  * Set zoom to next in list.
864  */
865 void
866 SPDesktop::next_zoom()
868     if (zooms_future == NULL) {
869         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
870         return;
871     }
873     // push current zoom into past zooms list
874     push_current_zoom (&zooms_past);
876     // restore next zoom
877     set_display_area (((NRRect *) zooms_future->data)->x0,
878             ((NRRect *) zooms_future->data)->y0,
879             ((NRRect *) zooms_future->data)->x1,
880             ((NRRect *) zooms_future->data)->y1,
881             0, false);
883     // remove the just-used zoom from the zooms_future list
884     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
887 /** \brief  Performs a quick zoom into what the user is working on
888     \param  enable  Whether we're going in or out of quick zoom
890 */
891 void
892 SPDesktop::zoom_quick (bool enable)
894     if (enable == _quick_zoom_enabled) {
895         return;
896     }
898     if (enable == true) {
899         _quick_zoom_stored_area = get_display_area();
900         bool zoomed = false;
902         // TODO This needs to migrate into the node tool, but currently the design
903         // of this method is sufficiently wrong to prevent this.
904         if (!zoomed && INK_IS_NODE_TOOL(event_context)) {
905             InkNodeTool *nt = static_cast<InkNodeTool*>(event_context);
906             if (!nt->_selected_nodes->empty()) {
907                 Geom::Rect nodes = *nt->_selected_nodes->bounds();
908                 double area = nodes.area();
909                 // do not zoom if a single cusp node is selected aand the bounds
910                 // have zero area.
911                 if (!Geom::are_near(area, 0) && area * 2.0 < _quick_zoom_stored_area.area()) {
912                     set_display_area(nodes, true);
913                     zoomed = true;
914                 }
915             }
916         }
918         if (!zoomed) {
919             Geom::OptRect const d = selection->bounds();
920             if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
921                 set_display_area(*d, true);
922                 zoomed = true;
923             }
924         }
926         if (!zoomed) {
927             zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
928             zoomed = true;
929         }
930     } else {
931         set_display_area(_quick_zoom_stored_area, false);
932     }
934     _quick_zoom_enabled = enable;
935     return;
938 /**
939  * Zoom to point with absolute zoom factor.
940  */
941 void
942 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
944     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
946     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
947     // this check prevents "sliding" when trying to zoom in at maximum zoom;
948     /// \todo someone please fix calculations properly and remove this hack
949     if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
950         return;
952     Geom::Rect const viewbox = canvas->getViewbox();
954     double const width2 = viewbox.dimensions()[Geom::X] / zoom;
955     double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
957     set_display_area(cx - px * width2,
958                      cy - py * height2,
959                      cx + (1 - px) * width2,
960                      cy + (1 - py) * height2,
961                      0.0);
964 /**
965   * Apply the desktop's current style or the tool style to the object.
966   */
967 void SPDesktop::applyCurrentOrToolStyle(SPObject *obj, Glib::ustring const &tool_path, bool with_text)
969         SPCSSAttr *css_current = sp_desktop_get_style(this, with_text);
970     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
972     if (prefs->getBool(tool_path + "/usecurrent") && css_current) {
973         //sp_repr_css_set(obj->getRepr(), css_current, "style");
974                 obj->setCSS(css_current,"style");
975     } else {
976         SPCSSAttr *css = prefs->getInheritedStyle(tool_path + "/style");
977         //sp_repr_css_set(obj->getRepr(), css, "style");
978                 obj->setCSS(css,"style");
979         sp_repr_css_attr_unref(css);
980     }
981     if (css_current) {
982         sp_repr_css_attr_unref(css_current);
983     }
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()[Geom::X]) {
1005         cx = area.min()[Geom::X];
1006     }
1007     if (cx > area.max()[Geom::X]) {
1008         cx = area.max()[Geom::X];
1009     }
1010     if (cy < area.min()[Geom::Y]) {
1011         cy = area.min()[Geom::Y];
1012     }
1013     if (cy > area.max()[Geom::Y]) {
1014         cy = area.max()[Geom::Y];
1015     }
1017     gdouble const scale = _d2w.descrim() * zoom;
1018     double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
1019     double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::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(doc()->getWidth(), doc()->getHeight()));
1043     if (d.minExtent() < 1.0) {
1044         return;
1045     }
1047     set_display_area(d, 10);
1050 /**
1051  * Set display area to current document width.
1052  */
1053 void
1054 SPDesktop::zoom_page_width()
1056     Geom::Rect const a = get_display_area();
1058     if (doc()->getWidth() < 1.0) {
1059         return;
1060     }
1062     Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1063                  Geom::Point(doc()->getWidth(), a.midpoint()[Geom::Y]));
1065     set_display_area(d, 10);
1068 /**
1069  * Zoom to selection.
1070  */
1071 void
1072 SPDesktop::zoom_selection()
1074     Geom::OptRect const d = selection->bounds();
1076     if ( !d || d->minExtent() < 0.1 ) {
1077         return;
1078     }
1080     set_display_area(*d, 10);
1083 /**
1084  * Tell widget to let zoom widget grab keyboard focus.
1085  */
1086 void
1087 SPDesktop::zoom_grab_focus()
1089     _widget->letZoomGrabFocus();
1092 /**
1093  * Zoom to whole drawing.
1094  */
1095 void
1096 SPDesktop::zoom_drawing()
1098     g_return_if_fail (doc() != NULL);
1099     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1100     g_return_if_fail (docitem != NULL);
1102     Geom::OptRect d = docitem->getBboxDesktop();
1104     /* Note that the second condition here indicates that
1105     ** there are no items in the drawing.
1106     */
1107     if ( !d || d->minExtent() < 0.1 ) {
1108         return;
1109     }
1111     set_display_area(*d, 10);
1114 /**
1115  * Scroll canvas by specific coordinate amount in svg coordinates.
1116  */
1117 void
1118 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1120     double scale = _d2w.descrim();
1121     scroll_world(dx*scale, dy*scale, is_scrolling);
1124 /**
1125  * Scroll canvas by specific coordinate amount.
1126  */
1127 void
1128 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1130     g_assert(_widget);
1132     Geom::Rect const viewbox = canvas->getViewbox();
1134     sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1136     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1137     sp_box3d_context_update_lines(event_context);
1139     _widget->updateRulers();
1140     _widget->updateScrollbars(_d2w.descrim());
1143 bool
1144 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1146     using Geom::X;
1147     using Geom::Y;
1149     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1150     gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1152     // autoscrolldistance is in screen pixels, but the display area is in document units
1153     autoscrolldistance /= _d2w.descrim();
1154     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1155     Geom::Rect dbox = get_display_area();
1156     dbox.expandBy(-autoscrolldistance);
1158     if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1159         !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y])   ) {
1161         Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1163         gdouble x_to;
1164         if (p[X] < dbox.min()[X])
1165             x_to = dbox.min()[X];
1166         else if (p[X] > dbox.max()[X])
1167             x_to = dbox.max()[X];
1168         else
1169             x_to = p[X];
1171         gdouble y_to;
1172         if (p[Y] < dbox.min()[Y])
1173             y_to = dbox.min()[Y];
1174         else if (p[Y] > dbox.max()[Y])
1175             y_to = dbox.max()[Y];
1176         else
1177             y_to = p[Y];
1179         Geom::Point const d_dt(x_to, y_to);
1180         Geom::Point const d_w( d_dt * _d2w );
1181         Geom::Point const moved_w( d_w - s_w );
1183         if (autoscrollspeed == 0)
1184             autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1186         if (autoscrollspeed != 0)
1187             scroll_world (autoscrollspeed * moved_w);
1189         return true;
1190     }
1191     return false;
1194 bool
1195 SPDesktop::is_iconified()
1197     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1200 void
1201 SPDesktop::iconify()
1203     _widget->setIconified();
1206 bool
1207 SPDesktop::is_maximized()
1209     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1212 void
1213 SPDesktop::maximize()
1215     _widget->setMaximized();
1218 bool
1219 SPDesktop::is_fullscreen()
1221     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1224 void
1225 SPDesktop::fullscreen()
1227     _widget->setFullscreen();
1230 /** \brief  Checks to see if the user is working in focused mode
1232     Returns the value of \c _focusMode
1233 */
1234 bool
1235 SPDesktop::is_focusMode()
1237     return _focusMode;
1240 /** \brief  Changes whether the user is in focus mode or not
1241     \param  mode  Which mode the view should be in
1243 */
1244 void
1245 SPDesktop::focusMode (bool mode)
1247     if (mode == _focusMode) { return; }
1249     _focusMode = mode;
1251     layoutWidget();
1252     //sp_desktop_widget_layout(SPDesktopWidget);
1254     return;
1257 void
1258 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1260     _widget->getGeometry (x, y, w, h);
1263 void
1264 SPDesktop::setWindowPosition (Geom::Point p)
1266     _widget->setPosition (p);
1269 void
1270 SPDesktop::setWindowSize (gint w, gint h)
1272     _widget->setSize (w, h);
1275 void
1276 SPDesktop::setWindowTransient (void *p, int transient_policy)
1278     _widget->setTransient (p, transient_policy);
1281 Gtk::Window*
1282 SPDesktop::getToplevel( )
1284     return _widget->getWindow();
1287 void
1288 SPDesktop::presentWindow()
1290     _widget->present();
1293 bool
1294 SPDesktop::warnDialog (gchar *text)
1296     return _widget->warnDialog (text);
1299 void
1300 SPDesktop::toggleRulers()
1302     _widget->toggleRulers();
1305 void
1306 SPDesktop::toggleScrollbars()
1308     _widget->toggleScrollbars();
1311 void
1312 SPDesktop::layoutWidget()
1314     _widget->layout();
1317 void
1318 SPDesktop::destroyWidget()
1320     _widget->destroy();
1323 bool
1324 SPDesktop::shutdown()
1326     return _widget->shutdown();
1329 bool SPDesktop::onDeleteUI (GdkEventAny*)
1331     if(shutdown())
1332         return true;
1334     destroyWidget();
1335     return false;
1338 /**
1339  *  onWindowStateEvent
1340  *
1341  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1342  *  Since GTK doesn't have a way to query this state information directly, we
1343  *  record it for the desktop here, and also possibly trigger a layout.
1344  */
1345 bool
1346 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1348     // Record the desktop window's state
1349     window_state = event->new_window_state;
1351     // Layout may differ depending on full-screen mode or not
1352     GdkWindowState changed = event->changed_mask;
1353     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1354         layoutWidget();
1355     }
1357     return false;
1360 void
1361 SPDesktop::setToolboxFocusTo (gchar const *label)
1363     _widget->setToolboxFocusTo (label);
1366 void
1367 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1369     _widget->setToolboxAdjustmentValue (id, val);
1372 void
1373 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1375     _widget->setToolboxSelectOneValue (id, val);
1378 bool
1379 SPDesktop::isToolboxButtonActive (gchar const *id)
1381     return _widget->isToolboxButtonActive (id);
1384 void
1385 SPDesktop::emitToolSubselectionChanged(gpointer data)
1387     _tool_subselection_changed.emit(data);
1388     inkscape_subselection_changed (this);
1391 void
1392 SPDesktop::updateNow()
1394   sp_canvas_update_now(canvas);
1397 void
1398 SPDesktop::enableInteraction()
1400   _widget->enableInteraction();
1403 void SPDesktop::disableInteraction()
1405   _widget->disableInteraction();
1408 void SPDesktop::setWaitingCursor()
1410     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1411     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1412     gdk_cursor_unref(waiting);
1413     // GDK needs the flush for the cursor change to take effect
1414     gdk_flush();
1415     waiting_cursor = true;
1418 void SPDesktop::clearWaitingCursor()
1420   if (waiting_cursor)
1421       sp_event_context_update_cursor(sp_desktop_event_context(this));
1424 void SPDesktop::toggleColorProfAdjust()
1426     _widget->toggleColorProfAdjust();
1429 void SPDesktop::toggleGrids()
1431     if (namedview->grids) {
1432         if(gridgroup) {
1433             showGrids(!grids_visible);
1434         }
1435     } else {
1436         //there is no grid present at the moment. add a rectangular grid and make it visible
1437         //Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1438         //Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1439                 namedview->writeNewGrid(sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1440         showGrids(true);
1441     }
1444 void SPDesktop::showGrids(bool show, bool dirty_document)
1446     grids_visible = show;
1447     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1448     if (show) {
1449         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1450     } else {
1451         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1452     }
1455 void SPDesktop::toggleSnapGlobal()
1457     //bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1458     //Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1459     //sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1460         bool v=namedview->getSnapGlobal();
1461         namedview->setSnapGlobal(!v);
1464 //----------------------------------------------------------------------
1465 // Callback implementations. The virtual ones are connected by the view.
1467 void
1468 SPDesktop::onPositionSet (double x, double y)
1470     _widget->viewSetPosition (Geom::Point(x,y));
1473 void
1474 SPDesktop::onResized (double /*x*/, double /*y*/)
1476    // Nothing called here
1479 /**
1480  * Redraw callback; queues Gtk redraw; connected by View.
1481  */
1482 void
1483 SPDesktop::onRedrawRequested ()
1485     if (main) {
1486         _widget->requestCanvasUpdate();
1487     }
1490 void
1491 SPDesktop::updateCanvasNow()
1493   _widget->requestCanvasUpdateAndWait();
1496 /**
1497  * Associate document with desktop.
1498  */
1499 void
1500 SPDesktop::setDocument (SPDocument *doc)
1502     if (this->doc() && doc) {
1503         namedview->hide(this);
1504         SP_ITEM (sp_document_root (this->doc()))->invoke_hide (dkey);
1505     }
1507     if (_layer_hierarchy) {
1508         _layer_hierarchy->clear();
1509         delete _layer_hierarchy;
1510     }
1511     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1512     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1513     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1514     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1515     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1517     /* setup EventLog */
1518     event_log = new Inkscape::EventLog(doc);
1519     doc->addUndoObserver(*event_log);
1521     _commit_connection.disconnect();
1522     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1524     /// \todo fixme: This condition exists to make sure the code
1525     /// inside is NOT called on initialization, only on replacement. But there
1526     /// are surely more safe methods to accomplish this.
1527     // TODO since the comment had reversed logic, check the intent of this block of code:
1528     if (drawing) {
1529         NRArenaItem *ai = 0;
1531         namedview = sp_document_namedview (doc, NULL);
1532         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1533         number = namedview->getViewCount();
1535         ai = SP_ITEM (sp_document_root (doc))->invoke_show (
1536                 SP_CANVAS_ARENA (drawing)->arena,
1537                 dkey,
1538                 SP_ITEM_SHOW_DISPLAY);
1539         if (ai) {
1540             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1541         }
1542         namedview->show(this);
1543         /* Ugly hack */
1544         activate_guides (true);
1545         /* Ugly hack */
1546         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1547     }
1549     _document_replaced_signal.emit (this, doc);
1551     View::setDocument (doc);
1554 void
1555 SPDesktop::onStatusMessage
1556 (Inkscape::MessageType type, gchar const *message)
1558     if (_widget) {
1559         _widget->setMessage(type, message);
1560     }
1563 void
1564 SPDesktop::onDocumentURISet (gchar const* uri)
1566     _widget->setTitle(uri);
1569 /**
1570  * Resized callback.
1571  */
1572 void
1573 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1575     _doc2dt[5] = height;
1576     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1577     Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1578     SP_CTRLRECT(page)->setRectangle(a);
1579     SP_CTRLRECT(page_border)->setRectangle(a);
1583 void
1584 SPDesktop::_onActivate (SPDesktop* dt)
1586     if (!dt->_widget) return;
1587     dt->_widget->activateDesktop();
1590 void
1591 SPDesktop::_onDeactivate (SPDesktop* dt)
1593     if (!dt->_widget) return;
1594     dt->_widget->deactivateDesktop();
1597 void
1598 SPDesktop::_onSelectionModified
1599 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1601     if (!dt->_widget) return;
1602     dt->_widget->updateScrollbars (dt->_d2w.descrim());
1605 static void
1606 _onSelectionChanged
1607 (Inkscape::Selection *selection, SPDesktop *desktop)
1609     /** \todo
1610      * only change the layer for single selections, or what?
1611      * This seems reasonable -- for multiple selections there can be many
1612      * different layers involved.
1613      */
1614     SPItem *item=selection->singleItem();
1615     if (item) {
1616         SPObject *layer=desktop->layerForObject(item);
1617         if ( layer && layer != desktop->currentLayer() ) {
1618             desktop->setCurrentLayer(layer);
1619         }
1620     }
1623 /**
1624  * Calls event handler of current event context.
1625  * \param arena Unused
1626  * \todo fixme
1627  */
1628 static gint
1629 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1631     if (ai) {
1632         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1633         return sp_event_context_item_handler (desktop->event_context, spi, event);
1634     } else {
1635         return sp_event_context_root_handler (desktop->event_context, event);
1636     }
1639 static void
1640 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1641     g_return_if_fail(SP_IS_GROUP(layer));
1642     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1645 /// Callback
1646 static void
1647 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1648     g_return_if_fail(SP_IS_GROUP(layer));
1649     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1652 /// Callback
1653 static void
1654 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1655                                          SPDesktop *desktop)
1657     desktop->_layer_changed_signal.emit (bottom);
1660 /// Called when document is starting to be rebuilt.
1661 static void
1662 _reconstruction_start (SPDesktop * desktop)
1664     // printf("Desktop, starting reconstruction\n");
1665     desktop->_reconstruction_old_layer_id = g_strdup(desktop->currentLayer()->getId());
1666     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1668     /*
1669     GSList const * selection_objs = desktop->selection->list();
1670     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1672     }
1673     */
1674     desktop->selection->clear();
1676     // printf("Desktop, starting reconstruction end\n");
1679 /// Called when document rebuild is finished.
1680 static void
1681 _reconstruction_finish (SPDesktop * desktop)
1683     // printf("Desktop, finishing reconstruction\n");
1684     if (desktop->_reconstruction_old_layer_id == NULL)
1685         return;
1687     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1688     if (newLayer != NULL)
1689         desktop->setCurrentLayer(newLayer);
1691     g_free(desktop->_reconstruction_old_layer_id);
1692     desktop->_reconstruction_old_layer_id = NULL;
1693     // printf("Desktop, finishing reconstruction end\n");
1694     return;
1697 /**
1698  * Namedview_modified callback.
1699  */
1700 static void
1701 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1703     SPNamedView *nv=SP_NAMEDVIEW(obj);
1705     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1707         /* Show/hide page background */
1708         if (nv->pagecolor & 0xff) {
1709             sp_canvas_item_show (desktop->table);
1710             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1711             sp_canvas_item_move_to_z (desktop->table, 0);
1712         } else {
1713             sp_canvas_item_hide (desktop->table);
1714         }
1716         /* Show/hide page border */
1717         if (nv->showborder) {
1718             // show
1719             sp_canvas_item_show (desktop->page_border);
1720             // set color and shadow
1721             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1722             if (nv->pageshadow) {
1723                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1724             }
1725             // place in the z-order stack
1726             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1727                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1728             } else {
1729                 int order = sp_canvas_item_order (desktop->page_border);
1730                 int morder = sp_canvas_item_order (desktop->drawing);
1731                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1732                     morder - order);
1733             }
1734         } else {
1735                 sp_canvas_item_hide (desktop->page_border);
1736                 if (nv->pageshadow) {
1737                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1738                 }
1739         }
1741         /* Show/hide page shadow */
1742         if (nv->showpageshadow && nv->pageshadow) {
1743             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1744         } else {
1745             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1746         }
1748         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1749         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1750             (SP_RGBA32_R_U(nv->pagecolor) +
1751              SP_RGBA32_G_U(nv->pagecolor) +
1752              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1753             // the background color is light or transparent, use black outline
1754             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1755         } else { // use white outline
1756             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1757         }
1758     }
1761 Geom::Matrix SPDesktop::w2d() const
1763     return _w2d;
1766 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1768     return p * _w2d;
1771 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1773     return p * _d2w;
1776 Geom::Matrix SPDesktop::doc2dt() const
1778     return _doc2dt;
1781 Geom::Matrix SPDesktop::dt2doc() const
1783     // doc2dt is its own inverse
1784     return _doc2dt;
1787 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1789     return p * _doc2dt;
1792 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1794     return p * dt2doc();
1798 /*
1799  * Pop event context from desktop's context stack. Never used.
1800  */
1801 // void
1802 // SPDesktop::pop_event_context (unsigned int key)
1803 // {
1804 //    SPEventContext *ec = NULL;
1805 //
1806 //    if (event_context && event_context->key == key) {
1807 //        g_return_if_fail (event_context);
1808 //        g_return_if_fail (event_context->next);
1809 //        ec = event_context;
1810 //        sp_event_context_deactivate (ec);
1811 //        event_context = ec->next;
1812 //        sp_event_context_activate (event_context);
1813 //        _event_context_changed_signal.emit (this, ec);
1814 //    }
1815 //
1816 //    SPEventContext *ref = event_context;
1817 //    while (ref && ref->next && ref->next->key != key)
1818 //        ref = ref->next;
1819 //
1820 //    if (ref && ref->next) {
1821 //        ec = ref->next;
1822 //        ref->next = ec->next;
1823 //    }
1824 //
1825 //    if (ec) {
1826 //        sp_event_context_finish (ec);
1827 //        g_object_unref (G_OBJECT (ec));
1828 //    }
1829 // }
1831 /*
1832   Local Variables:
1833   mode:c++
1834   c-file-style:"stroustrup"
1835   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1836   indent-tabs-mode:nil
1837   fill-column:99
1838   End:
1839 */
1840 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :