Code

Comment cleanup
[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 // TODO those includes are only for node tool quick zoom. Remove them after fixing it.
95 #include "ui/tool/node-tool.h"
96 #include "ui/tool/control-point-selection.h"
98 #include "display/sp-canvas.h"
100 namespace Inkscape { namespace XML { class Node; }}
102 // Callback declarations
103 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
104 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
105 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
106 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
107 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
108 static void _reconstruction_start(SPDesktop * desktop);
109 static void _reconstruction_finish(SPDesktop * desktop);
110 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
112 /**
113  * Return new desktop object.
114  * \pre namedview != NULL.
115  * \pre canvas != NULL.
116  */
117 SPDesktop::SPDesktop() :
118     _dlg_mgr( 0 ),
119     namedview( 0 ),
120     canvas( 0 ),
121     selection( 0 ),
122     event_context( 0 ),
123     layer_manager( 0 ),
124     event_log( 0 ),
125     temporary_item_list( 0 ),
126     snapindicator( 0 ),
127     acetate( 0 ),
128     main( 0 ),
129     gridgroup( 0 ),
130     guides( 0 ),
131     drawing( 0 ),
132     sketch( 0 ),
133     controls( 0 ),
134     tempgroup ( 0 ),
135     table( 0 ),
136     page( 0 ),
137     page_border( 0 ),
138     current( 0 ),
139     _focusMode(false),
140     zooms_past( 0 ),
141     zooms_future( 0 ),
142     dkey( 0 ),
143     number( 0 ),
144     window_state(0),
145     interaction_disabled_counter( 0 ),
146     waiting_cursor( false ),
147     guides_active( false ),
148     gr_item( 0 ),
149     gr_point_type( 0 ),
150     gr_point_i( 0 ),
151     gr_fill_or_stroke( true ),
152     _layer_hierarchy( 0 ),
153     _reconstruction_old_layer_id( 0 ),
154     _display_mode(Inkscape::RENDERMODE_NORMAL),
155     _widget( 0 ),
156     _inkscape( 0 ),
157     _guides_message_context( 0 ),
158     _active( false ),
159     _w2d(),
160     _d2w(),
161     _doc2dt( Geom::Scale(1, -1) ),
162     grids_visible( false )
164     _d2w.setIdentity();
165     _w2d.setIdentity();
167     selection = Inkscape::GC::release( new Inkscape::Selection(this) );
170 void
171 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas, Inkscape::UI::View::EditWidgetInterface *widget)
173     _widget = widget;
175     // Temporary workaround for link order issues:
176     Inkscape::DeviceManager::getManager().getDevices();
177     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
179     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
181     current = prefs->getStyle("/desktop/style");
183     namedview = nv;
184     canvas = aCanvas;
186     SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
187     /* Kill flicker */
188     sp_document_ensure_up_to_date (document);
190     /* Setup Dialog Manager */
191     _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
193     dkey = sp_item_display_key_new (1);
195     /* Connect document */
196     setDocument (document);
198     number = namedview->getViewCount();
201     /* Setup Canvas */
202     g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
204     SPCanvasGroup *root = sp_canvas_root (canvas);
206     /* Setup adminstrative layers */
207     acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
208     g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
209     main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
210     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
212     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
213     SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
214     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
215     sp_canvas_item_move_to_z (table, 0);
217     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
218     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
219     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
221     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
222     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
224     SP_CANVAS_ARENA (drawing)->arena->delta = prefs->getDouble("/options/cursortolerance/value", 1.0); // default is 1 px
226     if (prefs->getBool("/options/startmode/outline")) {
227         // Start in outline mode
228         setDisplayModeOutline();
229     } else {
230         // Start in normal mode, default
231         setDisplayModeNormal();
232     }
234     // The order in which these canvas items are added determines the z-order. It's therefore
235     // important to add the tempgroup (which will contain the snapindicator) before adding the
236     // controls. Only this way one will be able to quickly (before the snap indicator has
237     // disappeared) reselect a node after snapping it. If the z-order is wrong however, this
238     // will not work (the snap indicator is on top of the node handler; is the snapindicator
239     // being selected? or does it intercept some of the events that should have gone to the
240     // node handler? see bug https://bugs.launchpad.net/inkscape/+bug/414142)
241     gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
242     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
243     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
244     tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
245     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
247     /* Push select tool to the bottom of stack */
248     /** \todo
249      * FIXME: this is the only call to this.  Everything else seems to just
250      * call "set" instead of "push".  Can we assume that there is only one
251      * context ever?
252      */
253     push_event_context (SP_TYPE_SELECT_CONTEXT, "/tools/select", SP_EVENT_CONTEXT_STATIC);
255     // display rect and zoom are now handled in sp_desktop_widget_realize()
257     Geom::Rect const d(Geom::Point(0.0, 0.0),
258                        Geom::Point(sp_document_width(document), sp_document_height(document)));
260     SP_CTRLRECT(page)->setRectangle(d);
261     SP_CTRLRECT(page_border)->setRectangle(d);
263     /* the following sets the page shadow on the canvas
264        It was originally set to 5, which is really cheesy!
265        It now is an attribute in the document's namedview. If a value of
266        0 is used, then the constructor for a shadow is not initialized.
267     */
269     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
270         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
271     }
274     /* Connect event for page resize */
275     _doc2dt[5] = sp_document_height (document);
276     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
278     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
280     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
281             SP_CANVAS_ARENA (drawing)->arena,
282             dkey,
283             SP_ITEM_SHOW_DISPLAY);
284     if (ai) {
285         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
286     }
288     namedview->show(this);
289     /* Ugly hack */
290     activate_guides (true);
291     /* Ugly hack */
292     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
294 /* Set up notification of rebuilding the document, this allows
295        for saving object related settings in the document. */
296     _reconstruction_start_connection =
297         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
298     _reconstruction_finish_connection =
299         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
300     _reconstruction_old_layer_id = NULL;
302     // ?
303     // sp_active_desktop_set (desktop);
304     _inkscape = INKSCAPE;
306     _activate_connection = _activate_signal.connect(
307         sigc::bind(
308             sigc::ptr_fun(_onActivate),
309             this
310         )
311     );
312      _deactivate_connection = _deactivate_signal.connect(
313         sigc::bind(
314             sigc::ptr_fun(_onDeactivate),
315             this
316         )
317     );
319     _sel_modified_connection = selection->connectModified(
320         sigc::bind(
321             sigc::ptr_fun(&_onSelectionModified),
322             this
323         )
324     );
325     _sel_changed_connection = selection->connectChanged(
326         sigc::bind(
327             sigc::ptr_fun(&_onSelectionChanged),
328             this
329         )
330     );
333     /* setup LayerManager */
334     //   (Setting up after the connections are all in place, as it may use some of them)
335     layer_manager = new Inkscape::LayerManager( this );
337     showGrids(namedview->grids_visible, false);
339     temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
340     snapindicator = new Inkscape::Display::SnapIndicator ( this );
344 void SPDesktop::destroy()
346     if (snapindicator) {
347         delete snapindicator;
348         snapindicator = NULL;
349     }
350     if (temporary_item_list) {
351         delete temporary_item_list;
352         temporary_item_list = NULL;
353     }
355     if (selection) {
356         delete selection;
357         selection = NULL;
358     }
360     namedview->hide(this);
362     _activate_connection.disconnect();
363     _deactivate_connection.disconnect();
364     _sel_modified_connection.disconnect();
365     _sel_changed_connection.disconnect();
366     _modified_connection.disconnect();
367     _commit_connection.disconnect();
368     _reconstruction_start_connection.disconnect();
369     _reconstruction_finish_connection.disconnect();
371     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
372     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
373     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
375     while (event_context) {
376         SPEventContext *ec = event_context;
377         event_context = ec->next;
378         sp_event_context_finish (ec);
379         g_object_unref (G_OBJECT (ec));
380     }
382     if (_layer_hierarchy) {
383         delete _layer_hierarchy;
384 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
385     }
387     if (layer_manager) {
388         delete layer_manager;
389         layer_manager = NULL;
390     }
392     if (_inkscape) {
393         _inkscape = NULL;
394     }
396     if (drawing) {
397         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
398         drawing = NULL;
399     }
401     delete _guides_message_context;
402     _guides_message_context = NULL;
404     g_list_free (zooms_past);
405     g_list_free (zooms_future);
408 SPDesktop::~SPDesktop() {}
410 //--------------------------------------------------------------------
411 /* Public methods */
414 /* These methods help for temporarily showing things on-canvas.
415  * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
416  * is when you want to prematurely remove the item from the canvas, by calling
417  * desktop->remove_temporary_canvasitem(tempitem).
418  */
419 /** Note that lifetime is measured in milliseconds
420  * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
421  * delete the object for you and the reference will become invalid without you knowing it.
422  * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
423  * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
424  * because the object might be deleted already without you knowing it.
425  * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
426  */
427 Inkscape::Display::TemporaryItem *
428 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
430     if (move_to_bottom) {
431         sp_canvas_item_move_to_z(item, 0);
432     }
434     return temporary_item_list->add_item(item, lifetime);
437 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
438 */
439 void
440 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
442     // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
443     if (tempitem && temporary_item_list) {
444         temporary_item_list->delete_item(tempitem);
445     }
448 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
449     SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
450     canvas->rendermode = mode;
451     _display_mode = mode;
452     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
453     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
456 void SPDesktop::displayModeToggle() {
457     switch (_display_mode) {
458     case Inkscape::RENDERMODE_NORMAL:
459         _setDisplayMode(Inkscape::RENDERMODE_NO_FILTERS);
460         break;
461     case Inkscape::RENDERMODE_NO_FILTERS:
462         _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
463         break;
464     case Inkscape::RENDERMODE_OUTLINE:
465     default:
466         _setDisplayMode(Inkscape::RENDERMODE_NORMAL);
467     }
470 /**
471  * Returns current root (=bottom) layer.
472  */
473 SPObject *SPDesktop::currentRoot() const
475     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
478 /**
479  * Returns current top layer.
480  */
481 SPObject *SPDesktop::currentLayer() const
483     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
486 /**
487  * Sets the current layer of the desktop.
488  *
489  * Make \a object the top layer.
490  */
491 void SPDesktop::setCurrentLayer(SPObject *object) {
492     g_return_if_fail(SP_IS_GROUP(object));
493     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
494     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
495     _layer_hierarchy->setBottom(object);
498 void SPDesktop::toggleLayerSolo(SPObject *object) {
499     g_return_if_fail(SP_IS_GROUP(object));
500     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
502     bool othersShowing = false;
503     std::vector<SPObject*> layers;
504     for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
505         layers.push_back(obj);
506         othersShowing |= !SP_ITEM(obj)->isHidden();
507     }
508     for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
509         layers.push_back(obj);
510         othersShowing |= !SP_ITEM(obj)->isHidden();
511     }
514     if ( SP_ITEM(object)->isHidden() ) {
515         SP_ITEM(object)->setHidden(false);
516     }
518     for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
519         SP_ITEM(*it)->setHidden(othersShowing);
520     }
523 /**
524  * Return layer that contains \a object.
525  */
526 SPObject *SPDesktop::layerForObject(SPObject *object) {
527     g_return_val_if_fail(object != NULL, NULL);
529     SPObject *root=currentRoot();
530     object = SP_OBJECT_PARENT(object);
531     while ( object && object != root && !isLayer(object) ) {
532         object = SP_OBJECT_PARENT(object);
533     }
534     return object;
537 /**
538  * True if object is a layer.
539  */
540 bool SPDesktop::isLayer(SPObject *object) const {
541     return ( SP_IS_GROUP(object)
542              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
543                   == SPGroup::LAYER ) );
546 /**
547  * True if desktop viewport fully contains \a item's bbox.
548  */
549 bool SPDesktop::isWithinViewport (SPItem *item) const
551     Geom::Rect const viewport = get_display_area();
552     Geom::OptRect const bbox = sp_item_bbox_desktop(item);
553     if (bbox) {
554         return viewport.contains(*bbox);
555     } else {
556         return true;
557     }
560 ///
561 bool SPDesktop::itemIsHidden(SPItem const *item) const {
562     return item->isHidden(this->dkey);
565 /**
566  * Set activate property of desktop; emit signal if changed.
567  */
568 void
569 SPDesktop::set_active (bool new_active)
571     if (new_active != _active) {
572         _active = new_active;
573         if (new_active) {
574             _activate_signal.emit();
575         } else {
576             _deactivate_signal.emit();
577         }
578     }
581 /**
582  * Set activate status of current desktop's named view.
583  */
584 void
585 SPDesktop::activate_guides(bool activate)
587     guides_active = activate;
588     namedview->activateGuides(this, activate);
591 /**
592  * Make desktop switch documents.
593  */
594 void
595 SPDesktop::change_document (SPDocument *theDocument)
597     g_return_if_fail (theDocument != NULL);
599     /* unselect everything before switching documents */
600     selection->clear();
602     setDocument (theDocument);
604     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
605        (this can probably be done in a better way) */
606     Gtk::Window *parent = this->getToplevel();
607     g_assert(parent != NULL);
608     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
609     if (dtw) dtw->desktop = this;
610     sp_desktop_widget_update_namedview(dtw);
612     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
613     _document_replaced_signal.emit (this, theDocument);
616 /**
617  * Make desktop switch event contexts.
618  */
619 void
620 SPDesktop::set_event_context (GtkType type, const gchar *config)
622     SPEventContext *ec;
623     while (event_context) {
624         ec = event_context;
625         sp_event_context_deactivate (ec);
626         // we have to keep event_context valid during destruction - otherwise writing
627         // destructors is next to impossible
628         SPEventContext *next = ec->next;
629         sp_event_context_finish (ec);
630         g_object_unref (G_OBJECT (ec));
631         event_context = next;
632     }
634     ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
635     ec->next = event_context;
636     event_context = ec;
637     sp_event_context_activate (ec);
638     _event_context_changed_signal.emit (this, ec);
641 /**
642  * Push event context onto desktop's context stack.
643  */
644 void
645 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
647     SPEventContext *ref, *ec;
649     if (event_context && event_context->key == key) return;
650     ref = event_context;
651     while (ref && ref->next && ref->next->key != key) ref = ref->next;
652     if (ref && ref->next) {
653         ec = ref->next;
654         ref->next = ec->next;
655         sp_event_context_finish (ec);
656         g_object_unref (G_OBJECT (ec));
657     }
659     if (event_context) sp_event_context_deactivate (event_context);
660     ec = sp_event_context_new (type, this, config, key);
661     ec->next = event_context;
662     event_context = ec;
663     sp_event_context_activate (ec);
664     _event_context_changed_signal.emit (this, ec);
667 /**
668  * Sets the coordinate status to a given point
669  */
670 void
671 SPDesktop::set_coordinate_status (Geom::Point p) {
672     _widget->setCoordinateStatus(p);
675 /**
676  * \see sp_document_item_from_list_at_point_bottom()
677  */
678 SPItem *
679 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
681     g_return_val_if_fail (doc() != NULL, NULL);
682     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
685 /**
686  * \see sp_document_item_at_point()
687  */
688 SPItem *
689 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
691     g_return_val_if_fail (doc() != NULL, NULL);
692     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
695 /**
696  * \see sp_document_group_at_point()
697  */
698 SPItem *
699 SPDesktop::group_at_point (Geom::Point const p) const
701     g_return_val_if_fail (doc() != NULL, NULL);
702     return sp_document_group_at_point (doc(), dkey, p);
705 /**
706  * \brief  Returns the mouse point in document coordinates; if mouse is
707  * outside the canvas, returns the center of canvas viewpoint
708  */
709 Geom::Point
710 SPDesktop::point() const
712     Geom::Point p = _widget->getPointer();
713     Geom::Point pw = sp_canvas_window_to_world (canvas, p);
714     p = w2d(pw);
716     Geom::Rect const r = canvas->getViewbox();
718     Geom::Point r0 = w2d(r.min());
719     Geom::Point r1 = w2d(r.max());
721     if (p[Geom::X] >= r0[Geom::X] &&
722         p[Geom::X] <= r1[Geom::X] &&
723         p[Geom::Y] >= r1[Geom::Y] &&
724         p[Geom::Y] <= r0[Geom::Y])
725     {
726         return p;
727     } else {
728         return (r0 + r1) / 2;
729     }
732 /**
733  * Put current zoom data in history list.
734  */
735 void
736 SPDesktop::push_current_zoom (GList **history)
738     Geom::Rect const area = get_display_area();
740     NRRect *old_zoom = g_new(NRRect, 1);
741     old_zoom->x0 = area.min()[Geom::X];
742     old_zoom->x1 = area.max()[Geom::X];
743     old_zoom->y0 = area.min()[Geom::Y];
744     old_zoom->y1 = area.max()[Geom::Y];
745     if ( *history == NULL
746          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
747                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
748                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
749                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
750     {
751         *history = g_list_prepend (*history, old_zoom);
752     }
755 /**
756  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
757  */
758 void
759 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
761     g_assert(_widget);
763     // save the zoom
764     if (log) {
765         push_current_zoom(&zooms_past);
766         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
767         g_list_free (zooms_future);
768         zooms_future = NULL;
769     }
771     double const cx = 0.5 * (x0 + x1);
772     double const cy = 0.5 * (y0 + y1);
774     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
775     Geom::Rect viewbox = canvas->getViewbox();
776     viewbox.expandBy(-border);
778     double scale = _d2w.descrim();
779     double newscale;
780     if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
781         newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
782     } else {
783         newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
784     }
786     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
788     int clear = FALSE;
789     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
790         // zoom changed - set new zoom factors
791         _d2w = Geom::Scale(newscale, -newscale);
792         _w2d = Geom::Scale(1/newscale, 1/-newscale);
793         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
794         clear = TRUE;
795         signal_zoom_changed.emit(_d2w.descrim());
796     }
798     /* Calculate top left corner (in document pixels) */
799     x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
800     y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
802     /* Scroll */
803     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
805     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
806     sp_box3d_context_update_lines(event_context);
808     _widget->updateRulers();
809     _widget->updateScrollbars(_d2w.descrim());
810     _widget->updateZoom();
813 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
815     set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
818 /**
819  * Return viewbox dimensions.
820  */
821 Geom::Rect SPDesktop::get_display_area() const
823     Geom::Rect const viewbox = canvas->getViewbox();
825     double const scale = _d2w[0];
827     return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
828                       Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
831 /**
832  * Revert back to previous zoom if possible.
833  */
834 void
835 SPDesktop::prev_zoom()
837     if (zooms_past == NULL) {
838         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
839         return;
840     }
842     // push current zoom into forward zooms list
843     push_current_zoom (&zooms_future);
845     // restore previous zoom
846     set_display_area (((NRRect *) zooms_past->data)->x0,
847             ((NRRect *) zooms_past->data)->y0,
848             ((NRRect *) zooms_past->data)->x1,
849             ((NRRect *) zooms_past->data)->y1,
850             0, false);
852     // remove the just-added zoom from the past zooms list
853     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
856 /**
857  * Set zoom to next in list.
858  */
859 void
860 SPDesktop::next_zoom()
862     if (zooms_future == NULL) {
863         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
864         return;
865     }
867     // push current zoom into past zooms list
868     push_current_zoom (&zooms_past);
870     // restore next zoom
871     set_display_area (((NRRect *) zooms_future->data)->x0,
872             ((NRRect *) zooms_future->data)->y0,
873             ((NRRect *) zooms_future->data)->x1,
874             ((NRRect *) zooms_future->data)->y1,
875             0, false);
877     // remove the just-used zoom from the zooms_future list
878     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
881 /** \brief  Performs a quick zoom into what the user is working on
882     \param  enable  Whether we're going in or out of quick zoom
884 */
885 void
886 SPDesktop::zoom_quick (bool enable)
888     if (enable == _quick_zoom_enabled) {
889         return;
890     }
892     if (enable == true) {
893         _quick_zoom_stored_area = get_display_area();
894         bool zoomed = false;
896         // TODO This needs to migrate into the node tool, but currently the design
897         // of this method is sufficiently wrong to prevent this.
898         if (!zoomed && INK_IS_NODE_TOOL(event_context)) {
899             InkNodeTool *nt = static_cast<InkNodeTool*>(event_context);
900             if (!nt->_selected_nodes->empty()) {
901                 Geom::Rect nodes = *nt->_selected_nodes->bounds();
902                 double area = nodes.area();
903                 // do not zoom if a single cusp node is selected aand the bounds
904                 // have zero area.
905                 if (!Geom::are_near(area, 0) && area * 2.0 < _quick_zoom_stored_area.area()) {
906                     set_display_area(nodes, true);
907                     zoomed = true;
908                 }
909             }
910         }
912         if (!zoomed) {
913             Geom::OptRect const d = selection->bounds();
914             if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
915                 set_display_area(*d, true);
916                 zoomed = true;
917             }
918         }
920         if (!zoomed) {
921             zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
922             zoomed = true;
923         }
924     } else {
925         set_display_area(_quick_zoom_stored_area, false);
926     }
928     _quick_zoom_enabled = enable;
929     return;
932 /**
933  * Zoom to point with absolute zoom factor.
934  */
935 void
936 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
938     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
940     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
941     // this check prevents "sliding" when trying to zoom in at maximum zoom;
942     /// \todo someone please fix calculations properly and remove this hack
943     if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
944         return;
946     Geom::Rect const viewbox = canvas->getViewbox();
948     double const width2 = viewbox.dimensions()[Geom::X] / zoom;
949     double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
951     set_display_area(cx - px * width2,
952                      cy - py * height2,
953                      cx + (1 - px) * width2,
954                      cy + (1 - py) * height2,
955                      0.0);
958 /**
959  * Zoom to center with absolute zoom factor.
960  */
961 void
962 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
964     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
967 /**
968  * Zoom to point with relative zoom factor.
969  */
970 void
971 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
973     Geom::Rect const area = get_display_area();
975     if (cx < area.min()[Geom::X]) {
976         cx = area.min()[Geom::X];
977     }
978     if (cx > area.max()[Geom::X]) {
979         cx = area.max()[Geom::X];
980     }
981     if (cy < area.min()[Geom::Y]) {
982         cy = area.min()[Geom::Y];
983     }
984     if (cy > area.max()[Geom::Y]) {
985         cy = area.max()[Geom::Y];
986     }
988     gdouble const scale = _d2w.descrim() * zoom;
989     double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
990     double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
992     zoom_absolute_keep_point(cx, cy, px, py, scale);
995 /**
996  * Zoom to center with relative zoom factor.
997  */
998 void
999 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1001     gdouble scale = _d2w.descrim() * zoom;
1002     zoom_absolute (cx, cy, scale);
1005 /**
1006  * Set display area to origin and current document dimensions.
1007  */
1008 void
1009 SPDesktop::zoom_page()
1011     Geom::Rect d(Geom::Point(0, 0),
1012                  Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1014     if (d.minExtent() < 1.0) {
1015         return;
1016     }
1018     set_display_area(d, 10);
1021 /**
1022  * Set display area to current document width.
1023  */
1024 void
1025 SPDesktop::zoom_page_width()
1027     Geom::Rect const a = get_display_area();
1029     if (sp_document_width(doc()) < 1.0) {
1030         return;
1031     }
1033     Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1034                  Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1036     set_display_area(d, 10);
1039 /**
1040  * Zoom to selection.
1041  */
1042 void
1043 SPDesktop::zoom_selection()
1045     Geom::OptRect const d = selection->bounds();
1047     if ( !d || d->minExtent() < 0.1 ) {
1048         return;
1049     }
1051     set_display_area(*d, 10);
1054 /**
1055  * Tell widget to let zoom widget grab keyboard focus.
1056  */
1057 void
1058 SPDesktop::zoom_grab_focus()
1060     _widget->letZoomGrabFocus();
1063 /**
1064  * Zoom to whole drawing.
1065  */
1066 void
1067 SPDesktop::zoom_drawing()
1069     g_return_if_fail (doc() != NULL);
1070     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1071     g_return_if_fail (docitem != NULL);
1073     Geom::OptRect d = sp_item_bbox_desktop(docitem);
1075     /* Note that the second condition here indicates that
1076     ** there are no items in the drawing.
1077     */
1078     if ( !d || d->minExtent() < 0.1 ) {
1079         return;
1080     }
1082     set_display_area(*d, 10);
1085 /**
1086  * Scroll canvas by specific coordinate amount in svg coordinates.
1087  */
1088 void
1089 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1091     double scale = _d2w.descrim();
1092     scroll_world(dx*scale, dy*scale, is_scrolling);
1095 /**
1096  * Scroll canvas by specific coordinate amount.
1097  */
1098 void
1099 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1101     g_assert(_widget);
1103     Geom::Rect const viewbox = canvas->getViewbox();
1105     sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1107     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1108     sp_box3d_context_update_lines(event_context);
1110     _widget->updateRulers();
1111     _widget->updateScrollbars(_d2w.descrim());
1114 bool
1115 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1117     using Geom::X;
1118     using Geom::Y;
1120     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1121     gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1123     // autoscrolldistance is in screen pixels, but the display area is in document units
1124     autoscrolldistance /= _d2w.descrim();
1125     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1126     Geom::Rect dbox = get_display_area();
1127     dbox.expandBy(-autoscrolldistance);
1129     if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1130         !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y])   ) {
1132         Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1134         gdouble x_to;
1135         if (p[X] < dbox.min()[X])
1136             x_to = dbox.min()[X];
1137         else if (p[X] > dbox.max()[X])
1138             x_to = dbox.max()[X];
1139         else
1140             x_to = p[X];
1142         gdouble y_to;
1143         if (p[Y] < dbox.min()[Y])
1144             y_to = dbox.min()[Y];
1145         else if (p[Y] > dbox.max()[Y])
1146             y_to = dbox.max()[Y];
1147         else
1148             y_to = p[Y];
1150         Geom::Point const d_dt(x_to, y_to);
1151         Geom::Point const d_w( d_dt * _d2w );
1152         Geom::Point const moved_w( d_w - s_w );
1154         if (autoscrollspeed == 0)
1155             autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1157         if (autoscrollspeed != 0)
1158             scroll_world (autoscrollspeed * moved_w);
1160         return true;
1161     }
1162     return false;
1165 bool
1166 SPDesktop::is_iconified()
1168     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1171 void
1172 SPDesktop::iconify()
1174     _widget->setIconified();
1177 bool
1178 SPDesktop::is_maximized()
1180     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1183 void
1184 SPDesktop::maximize()
1186     _widget->setMaximized();
1189 bool
1190 SPDesktop::is_fullscreen()
1192     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1195 void
1196 SPDesktop::fullscreen()
1198     _widget->setFullscreen();
1201 /** \brief  Checks to see if the user is working in focused mode
1203     Returns the value of \c _focusMode
1204 */
1205 bool
1206 SPDesktop::is_focusMode()
1208     return _focusMode;
1211 /** \brief  Changes whether the user is in focus mode or not
1212     \param  mode  Which mode the view should be in
1214 */
1215 void
1216 SPDesktop::focusMode (bool mode)
1218     if (mode == _focusMode) { return; }
1220     _focusMode = mode;
1222     layoutWidget();
1223     //sp_desktop_widget_layout(SPDesktopWidget);
1225     return;
1228 void
1229 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1231     _widget->getGeometry (x, y, w, h);
1234 void
1235 SPDesktop::setWindowPosition (Geom::Point p)
1237     _widget->setPosition (p);
1240 void
1241 SPDesktop::setWindowSize (gint w, gint h)
1243     _widget->setSize (w, h);
1246 void
1247 SPDesktop::setWindowTransient (void *p, int transient_policy)
1249     _widget->setTransient (p, transient_policy);
1252 Gtk::Window*
1253 SPDesktop::getToplevel( )
1255     return _widget->getWindow();
1258 void
1259 SPDesktop::presentWindow()
1261     _widget->present();
1264 bool
1265 SPDesktop::warnDialog (gchar *text)
1267     return _widget->warnDialog (text);
1270 void
1271 SPDesktop::toggleRulers()
1273     _widget->toggleRulers();
1276 void
1277 SPDesktop::toggleScrollbars()
1279     _widget->toggleScrollbars();
1282 void
1283 SPDesktop::layoutWidget()
1285     _widget->layout();
1288 void
1289 SPDesktop::destroyWidget()
1291     _widget->destroy();
1294 bool
1295 SPDesktop::shutdown()
1297     return _widget->shutdown();
1300 bool SPDesktop::onDeleteUI (GdkEventAny*)
1302     if(shutdown())
1303         return true;
1305     destroyWidget();
1306     return false;
1309 /**
1310  *  onWindowStateEvent
1311  *
1312  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1313  *  Since GTK doesn't have a way to query this state information directly, we
1314  *  record it for the desktop here, and also possibly trigger a layout.
1315  */
1316 bool
1317 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1319     // Record the desktop window's state
1320     window_state = event->new_window_state;
1322     // Layout may differ depending on full-screen mode or not
1323     GdkWindowState changed = event->changed_mask;
1324     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1325         layoutWidget();
1326     }
1328     return false;
1331 void
1332 SPDesktop::setToolboxFocusTo (gchar const *label)
1334     _widget->setToolboxFocusTo (label);
1337 void
1338 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1340     _widget->setToolboxAdjustmentValue (id, val);
1343 void
1344 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1346     _widget->setToolboxSelectOneValue (id, val);
1349 bool
1350 SPDesktop::isToolboxButtonActive (gchar const *id)
1352     return _widget->isToolboxButtonActive (id);
1355 void
1356 SPDesktop::emitToolSubselectionChanged(gpointer data)
1358     _tool_subselection_changed.emit(data);
1359     inkscape_subselection_changed (this);
1362 void
1363 SPDesktop::updateNow()
1365   sp_canvas_update_now(canvas);
1368 void
1369 SPDesktop::enableInteraction()
1371   _widget->enableInteraction();
1374 void SPDesktop::disableInteraction()
1376   _widget->disableInteraction();
1379 void SPDesktop::setWaitingCursor()
1381     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1382     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1383     gdk_cursor_unref(waiting);
1384     // GDK needs the flush for the cursor change to take effect
1385     gdk_flush();
1386     waiting_cursor = true;
1389 void SPDesktop::clearWaitingCursor()
1391   if (waiting_cursor)
1392       sp_event_context_update_cursor(sp_desktop_event_context(this));
1395 void SPDesktop::toggleColorProfAdjust()
1397     _widget->toggleColorProfAdjust();
1400 void SPDesktop::toggleGrids()
1402     if (namedview->grids) {
1403         if(gridgroup) {
1404             showGrids(!grids_visible);
1405         }
1406     } else {
1407         //there is no grid present at the moment. add a rectangular grid and make it visible
1408         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1409         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1410         showGrids(true);
1411     }
1414 void SPDesktop::showGrids(bool show, bool dirty_document)
1416     grids_visible = show;
1417     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1418     if (show) {
1419         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1420     } else {
1421         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1422     }
1425 void SPDesktop::toggleSnapGlobal()
1427     bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1428     Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1429     sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1432 //----------------------------------------------------------------------
1433 // Callback implementations. The virtual ones are connected by the view.
1435 void
1436 SPDesktop::onPositionSet (double x, double y)
1438     _widget->viewSetPosition (Geom::Point(x,y));
1441 void
1442 SPDesktop::onResized (double /*x*/, double /*y*/)
1444    // Nothing called here
1447 /**
1448  * Redraw callback; queues Gtk redraw; connected by View.
1449  */
1450 void
1451 SPDesktop::onRedrawRequested ()
1453     if (main) {
1454         _widget->requestCanvasUpdate();
1455     }
1458 void
1459 SPDesktop::updateCanvasNow()
1461   _widget->requestCanvasUpdateAndWait();
1464 /**
1465  * Associate document with desktop.
1466  */
1467 void
1468 SPDesktop::setDocument (SPDocument *doc)
1470     if (this->doc() && doc) {
1471         namedview->hide(this);
1472         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1473     }
1475     if (_layer_hierarchy) {
1476         _layer_hierarchy->clear();
1477         delete _layer_hierarchy;
1478     }
1479     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1480     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1481     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1482     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1483     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1485     /* setup EventLog */
1486     event_log = new Inkscape::EventLog(doc);
1487     doc->addUndoObserver(*event_log);
1489     _commit_connection.disconnect();
1490     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1492     /// \todo fixme: This condition exists to make sure the code
1493     /// inside is NOT called on initialization, only on replacement. But there
1494     /// are surely more safe methods to accomplish this.
1495     // TODO since the comment had reversed logic, check the intent of this block of code:
1496     if (drawing) {
1497         NRArenaItem *ai = 0;
1499         namedview = sp_document_namedview (doc, NULL);
1500         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1501         number = namedview->getViewCount();
1503         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1504                 SP_CANVAS_ARENA (drawing)->arena,
1505                 dkey,
1506                 SP_ITEM_SHOW_DISPLAY);
1507         if (ai) {
1508             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1509         }
1510         namedview->show(this);
1511         /* Ugly hack */
1512         activate_guides (true);
1513         /* Ugly hack */
1514         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1515     }
1517     _document_replaced_signal.emit (this, doc);
1519     View::setDocument (doc);
1522 void
1523 SPDesktop::onStatusMessage
1524 (Inkscape::MessageType type, gchar const *message)
1526     if (_widget) {
1527         _widget->setMessage(type, message);
1528     }
1531 void
1532 SPDesktop::onDocumentURISet (gchar const* uri)
1534     _widget->setTitle(uri);
1537 /**
1538  * Resized callback.
1539  */
1540 void
1541 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1543     _doc2dt[5] = height;
1544     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1545     Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1546     SP_CTRLRECT(page)->setRectangle(a);
1547     SP_CTRLRECT(page_border)->setRectangle(a);
1551 void
1552 SPDesktop::_onActivate (SPDesktop* dt)
1554     if (!dt->_widget) return;
1555     dt->_widget->activateDesktop();
1558 void
1559 SPDesktop::_onDeactivate (SPDesktop* dt)
1561     if (!dt->_widget) return;
1562     dt->_widget->deactivateDesktop();
1565 void
1566 SPDesktop::_onSelectionModified
1567 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1569     if (!dt->_widget) return;
1570     dt->_widget->updateScrollbars (dt->_d2w.descrim());
1573 static void
1574 _onSelectionChanged
1575 (Inkscape::Selection *selection, SPDesktop *desktop)
1577     /** \todo
1578      * only change the layer for single selections, or what?
1579      * This seems reasonable -- for multiple selections there can be many
1580      * different layers involved.
1581      */
1582     SPItem *item=selection->singleItem();
1583     if (item) {
1584         SPObject *layer=desktop->layerForObject(item);
1585         if ( layer && layer != desktop->currentLayer() ) {
1586             desktop->setCurrentLayer(layer);
1587         }
1588     }
1591 /**
1592  * Calls event handler of current event context.
1593  * \param arena Unused
1594  * \todo fixme
1595  */
1596 static gint
1597 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1599     if (ai) {
1600         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1601         return sp_event_context_item_handler (desktop->event_context, spi, event);
1602     } else {
1603         return sp_event_context_root_handler (desktop->event_context, event);
1604     }
1607 static void
1608 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1609     g_return_if_fail(SP_IS_GROUP(layer));
1610     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1613 /// Callback
1614 static void
1615 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1616     g_return_if_fail(SP_IS_GROUP(layer));
1617     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1620 /// Callback
1621 static void
1622 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1623                                          SPDesktop *desktop)
1625     desktop->_layer_changed_signal.emit (bottom);
1628 /// Called when document is starting to be rebuilt.
1629 static void
1630 _reconstruction_start (SPDesktop * desktop)
1632     // printf("Desktop, starting reconstruction\n");
1633     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1634     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1636     /*
1637     GSList const * selection_objs = desktop->selection->list();
1638     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1640     }
1641     */
1642     desktop->selection->clear();
1644     // printf("Desktop, starting reconstruction end\n");
1647 /// Called when document rebuild is finished.
1648 static void
1649 _reconstruction_finish (SPDesktop * desktop)
1651     // printf("Desktop, finishing reconstruction\n");
1652     if (desktop->_reconstruction_old_layer_id == NULL)
1653         return;
1655     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1656     if (newLayer != NULL)
1657         desktop->setCurrentLayer(newLayer);
1659     g_free(desktop->_reconstruction_old_layer_id);
1660     desktop->_reconstruction_old_layer_id = NULL;
1661     // printf("Desktop, finishing reconstruction end\n");
1662     return;
1665 /**
1666  * Namedview_modified callback.
1667  */
1668 static void
1669 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1671     SPNamedView *nv=SP_NAMEDVIEW(obj);
1673     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1675         /* Show/hide page background */
1676         if (nv->pagecolor & 0xff) {
1677             sp_canvas_item_show (desktop->table);
1678             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1679             sp_canvas_item_move_to_z (desktop->table, 0);
1680         } else {
1681             sp_canvas_item_hide (desktop->table);
1682         }
1684         /* Show/hide page border */
1685         if (nv->showborder) {
1686             // show
1687             sp_canvas_item_show (desktop->page_border);
1688             // set color and shadow
1689             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1690             if (nv->pageshadow) {
1691                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1692             }
1693             // place in the z-order stack
1694             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1695                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1696             } else {
1697                 int order = sp_canvas_item_order (desktop->page_border);
1698                 int morder = sp_canvas_item_order (desktop->drawing);
1699                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1700                     morder - order);
1701             }
1702         } else {
1703                 sp_canvas_item_hide (desktop->page_border);
1704                 if (nv->pageshadow) {
1705                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1706                 }
1707         }
1709         /* Show/hide page shadow */
1710         if (nv->showpageshadow && nv->pageshadow) {
1711             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1712         } else {
1713             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1714         }
1716         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1717         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1718             (SP_RGBA32_R_U(nv->pagecolor) +
1719              SP_RGBA32_G_U(nv->pagecolor) +
1720              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1721             // the background color is light or transparent, use black outline
1722             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1723         } else { // use white outline
1724             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1725         }
1726     }
1729 Geom::Matrix SPDesktop::w2d() const
1731     return _w2d;
1734 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1736     return p * _w2d;
1739 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1741     return p * _d2w;
1744 Geom::Matrix SPDesktop::doc2dt() const
1746     return _doc2dt;
1749 Geom::Matrix SPDesktop::dt2doc() const
1751     // doc2dt is its own inverse
1752     return _doc2dt;
1755 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1757     return p * _doc2dt;
1760 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1762     return p * dt2doc();
1766 /*
1767  * Pop event context from desktop's context stack. Never used.
1768  */
1769 // void
1770 // SPDesktop::pop_event_context (unsigned int key)
1771 // {
1772 //    SPEventContext *ec = NULL;
1773 //
1774 //    if (event_context && event_context->key == key) {
1775 //        g_return_if_fail (event_context);
1776 //        g_return_if_fail (event_context->next);
1777 //        ec = event_context;
1778 //        sp_event_context_deactivate (ec);
1779 //        event_context = ec->next;
1780 //        sp_event_context_activate (event_context);
1781 //        _event_context_changed_signal.emit (this, ec);
1782 //    }
1783 //
1784 //    SPEventContext *ref = event_context;
1785 //    while (ref && ref->next && ref->next->key != key)
1786 //        ref = ref->next;
1787 //
1788 //    if (ref && ref->next) {
1789 //        ec = ref->next;
1790 //        ref->next = ec->next;
1791 //    }
1792 //
1793 //    if (ec) {
1794 //        sp_event_context_finish (ec);
1795 //        g_object_unref (G_OBJECT (ec));
1796 //    }
1797 // }
1799 /*
1800   Local Variables:
1801   mode:c++
1802   c-file-style:"stroustrup"
1803   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1804   indent-tabs-mode:nil
1805   fill-column:99
1806   End:
1807 */
1808 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :