Code

now that selection description includes style (filtered, clipped), we need to update...
[inkscape.git] / src / desktop.cpp
1 #define __SP_DESKTOP_C__
3 /** \file
4  * Editable view implementation
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   MenTaLguY <mental@rydia.net>
9  *   bulia byak <buliabyak@users.sf.net>
10  *   Ralf Stephan <ralf@ark.in-berlin.de>
11  *   John Bintz <jcoswell@coswellproductions.org>
12  *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
13  *
14  * Copyright (C) 2007 Jon A. Cruz
15  * Copyright (C) 2006-2008 Johan Engelen
16  * Copyright (C) 2006 John Bintz
17  * Copyright (C) 2004 MenTaLguY
18  * Copyright (C) 1999-2002 Lauris Kaplinski
19  * Copyright (C) 2000-2001 Ximian, Inc.
20  *
21  * Released under GNU GPL, read the file 'COPYING' for more information
22  */
24 /** \class SPDesktop
25  * SPDesktop is a subclass of View, implementing an editable document
26  * canvas.  It is extensively used by many UI controls that need certain
27  * visual representations of their own.
28  *
29  * SPDesktop provides a certain set of SPCanvasItems, serving as GUI
30  * layers of different control objects. The one containing the whole
31  * document is the drawing layer. In addition to it, there are grid,
32  * guide, sketch and control layers. The sketch layer is used for
33  * temporary drawing objects, before the real objects in document are
34  * created. The control layer contains editing knots, rubberband and
35  * similar non-document UI objects.
36  *
37  * Each SPDesktop is associated with a SPNamedView node of the document
38  * tree.  Currently, all desktops are created from a single main named
39  * view, but in the future there may be support for different ones.
40  * SPNamedView serves as an in-document container for desktop-related
41  * data, like grid and guideline placement, snapping options and so on.
42  *
43  * Associated with each SPDesktop are the two most important editing
44  * related objects - SPSelection and SPEventContext.
45  *
46  * Sodipodi keeps track of the active desktop and invokes notification
47  * signals whenever it changes. UI elements can use these to update their
48  * display to the selection of the currently active editing window.
49  * (Lauris Kaplinski)
50  */
52 #ifdef HAVE_CONFIG_H
53 # include "config.h"
54 #endif
56 #include <glibmm/i18n.h>
57 #include <sigc++/functors/mem_fun.h>
58 #include <gtkmm.h>
60 #include <2geom/rect.h>
61 #include "macros.h"
62 #include "inkscape-private.h"
63 #include "desktop.h"
64 #include "desktop-events.h"
65 #include "desktop-handles.h"
66 #include "document.h"
67 #include "message-stack.h"
68 #include "selection.h"
69 #include "select-context.h"
70 #include "sp-namedview.h"
71 #include "color.h"
72 #include "sp-item-group.h"
73 #include "preferences.h"
74 #include "object-hierarchy.h"
75 #include "helper/units.h"
76 #include "display/canvas-arena.h"
77 #include "display/nr-arena.h"
78 #include "display/gnome-canvas-acetate.h"
79 #include "display/sodipodi-ctrlrect.h"
80 #include "display/sp-canvas-util.h"
81 #include "display/canvas-temporary-item-list.h"
82 #include "display/snap-indicator.h"
83 #include "ui/dialog/dialog-manager.h"
84 #include "xml/repr.h"
85 #include "message-context.h"
86 #include "device-manager.h"
87 #include "layer-fns.h"
88 #include "layer-manager.h"
89 #include "event-log.h"
90 #include "display/canvas-grid.h"
91 #include "widgets/desktop-widget.h"
92 #include "box3d-context.h"
94 #include "display/sp-canvas.h"
96 namespace Inkscape { namespace XML { class Node; }}
98 // Callback declarations
99 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
100 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
101 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
102 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
103 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
104 static void _reconstruction_start(SPDesktop * desktop);
105 static void _reconstruction_finish(SPDesktop * desktop);
106 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
108 /**
109  * Return new desktop object.
110  * \pre namedview != NULL.
111  * \pre canvas != NULL.
112  */
113 SPDesktop::SPDesktop() :
114     _dlg_mgr( 0 ),
115     namedview( 0 ),
116     canvas( 0 ),
117     selection( 0 ),
118     event_context( 0 ),
119     layer_manager( 0 ),
120     event_log( 0 ),
121     temporary_item_list( 0 ),
122     snapindicator( 0 ),
123     acetate( 0 ),
124     main( 0 ),
125     gridgroup( 0 ),
126     guides( 0 ),
127     drawing( 0 ),
128     sketch( 0 ),
129     controls( 0 ),
130     tempgroup ( 0 ),
131     table( 0 ),
132     page( 0 ),
133     page_border( 0 ),
134     current( 0 ),
135     _focusMode(false),
136     zooms_past( 0 ),
137     zooms_future( 0 ),
138     dkey( 0 ),
139     number( 0 ),
140     window_state(0),
141     interaction_disabled_counter( 0 ),
142     waiting_cursor( false ),
143     guides_active( false ),
144     gr_item( 0 ),
145     gr_point_type( 0 ),
146     gr_point_i( 0 ),
147     gr_fill_or_stroke( true ),
148     _layer_hierarchy( 0 ),
149     _reconstruction_old_layer_id( 0 ),
150     _display_mode(Inkscape::RENDERMODE_NORMAL),
151     _saved_display_mode(Inkscape::RENDERMODE_NORMAL),
152     _widget( 0 ),
153     _inkscape( 0 ),
154     _guides_message_context( 0 ),
155     _active( false ),
156     _w2d(),
157     _d2w(),
158     _doc2dt( Geom::Scale(1, -1) ),
159     grids_visible( false )
161     _d2w.setIdentity();
162     _w2d.setIdentity();
164     selection = Inkscape::GC::release( new Inkscape::Selection(this) );
167 void
168 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
170     // Temporary workaround for link order issues:
171     Inkscape::DeviceManager::getManager().getDevices();
172     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
174     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
176     current = prefs->getStyle("/desktop/style");
178     namedview = nv;
179     canvas = aCanvas;
181     SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
182     /* Kill flicker */
183     sp_document_ensure_up_to_date (document);
185     /* Setup Dialog Manager */
186     _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
188     dkey = sp_item_display_key_new (1);
190     /* Connect document */
191     setDocument (document);
193     number = namedview->getViewCount();
196     /* Setup Canvas */
197     g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
199     SPCanvasGroup *root = sp_canvas_root (canvas);
201     /* Setup adminstrative layers */
202     acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
203     g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
204     main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
205     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
207     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
208     SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
209     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
210     sp_canvas_item_move_to_z (table, 0);
212     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
213     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
214     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
216     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
217     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
219     SP_CANVAS_ARENA (drawing)->arena->delta = prefs->getDouble("/options/cursortolerance/value", 1.0); // default is 1 px
221     if (prefs->getBool("/options/startmode/outline")) {
222         // Start in outline mode
223         setDisplayModeOutline();
224     } else {
225         // Start in normal mode, default
226         setDisplayModeNormal();
227     }
229     gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
230     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
231     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
232     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
233     tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
235     /* Push select tool to the bottom of stack */
236     /** \todo
237      * FIXME: this is the only call to this.  Everything else seems to just
238      * call "set" instead of "push".  Can we assume that there is only one
239      * context ever?
240      */
241     push_event_context (SP_TYPE_SELECT_CONTEXT, "/tools/select", SP_EVENT_CONTEXT_STATIC);
243     // display rect and zoom are now handled in sp_desktop_widget_realize()
245     Geom::Rect const d(Geom::Point(0.0, 0.0),
246                        Geom::Point(sp_document_width(document), sp_document_height(document)));
248     SP_CTRLRECT(page)->setRectangle(d);
249     SP_CTRLRECT(page_border)->setRectangle(d);
251     /* the following sets the page shadow on the canvas
252        It was originally set to 5, which is really cheesy!
253        It now is an attribute in the document's namedview. If a value of
254        0 is used, then the constructor for a shadow is not initialized.
255     */
257     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
258         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
259     }
262     /* Connect event for page resize */
263     _doc2dt[5] = sp_document_height (document);
264     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
266     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
268     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
269             SP_CANVAS_ARENA (drawing)->arena,
270             dkey,
271             SP_ITEM_SHOW_DISPLAY);
272     if (ai) {
273         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
274     }
276     namedview->show(this);
277     /* Ugly hack */
278     activate_guides (true);
279     /* Ugly hack */
280     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
282 /* Set up notification of rebuilding the document, this allows
283        for saving object related settings in the document. */
284     _reconstruction_start_connection =
285         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
286     _reconstruction_finish_connection =
287         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
288     _reconstruction_old_layer_id = NULL;
290     // ?
291     // sp_active_desktop_set (desktop);
292     _inkscape = INKSCAPE;
294     _activate_connection = _activate_signal.connect(
295         sigc::bind(
296             sigc::ptr_fun(_onActivate),
297             this
298         )
299     );
300      _deactivate_connection = _deactivate_signal.connect(
301         sigc::bind(
302             sigc::ptr_fun(_onDeactivate),
303             this
304         )
305     );
307     _sel_modified_connection = selection->connectModified(
308         sigc::bind(
309             sigc::ptr_fun(&_onSelectionModified),
310             this
311         )
312     );
313     _sel_changed_connection = selection->connectChanged(
314         sigc::bind(
315             sigc::ptr_fun(&_onSelectionChanged),
316             this
317         )
318     );
321     /* setup LayerManager */
322     //   (Setting up after the connections are all in place, as it may use some of them)
323     layer_manager = new Inkscape::LayerManager( this );
325     showGrids(namedview->grids_visible, false);
327     temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
328     snapindicator = new Inkscape::Display::SnapIndicator ( this );
332 void SPDesktop::destroy()
334     if (snapindicator) {
335         delete snapindicator;
336         snapindicator = NULL;
337     }
338     if (temporary_item_list) {
339         delete temporary_item_list;
340         temporary_item_list = NULL;
341     }
343     if (selection) {
344         delete selection;
345         selection = NULL;
346     }
348     namedview->hide(this);
350     _activate_connection.disconnect();
351     _deactivate_connection.disconnect();
352     _sel_modified_connection.disconnect();
353     _sel_changed_connection.disconnect();
354     _modified_connection.disconnect();
355     _commit_connection.disconnect();
356     _reconstruction_start_connection.disconnect();
357     _reconstruction_finish_connection.disconnect();
359     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
360     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
361     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
363     while (event_context) {
364         SPEventContext *ec = event_context;
365         event_context = ec->next;
366         sp_event_context_finish (ec);
367         g_object_unref (G_OBJECT (ec));
368     }
370     if (_layer_hierarchy) {
371         delete _layer_hierarchy;
372 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
373     }
375     if (layer_manager) {
376         delete layer_manager;
377         layer_manager = NULL;
378     }
380     if (_inkscape) {
381         _inkscape = NULL;
382     }
384     if (drawing) {
385         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
386         drawing = NULL;
387     }
389     delete _guides_message_context;
390     _guides_message_context = NULL;
392     g_list_free (zooms_past);
393     g_list_free (zooms_future);
396 SPDesktop::~SPDesktop() {}
398 //--------------------------------------------------------------------
399 /* Public methods */
402 /* These methods help for temporarily showing things on-canvas.
403  * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
404  * is when you want to prematurely remove the item from the canvas, by calling
405  * desktop->remove_temporary_canvasitem(tempitem).
406  */
407 /** Note that lifetime is measured in milliseconds
408  * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
409  * delete the object for you and the reference will become invalid without you knowing it.
410  * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
411  * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
412  * because the object might be deleted already without you knowing it.
413  * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
414  */
415 Inkscape::Display::TemporaryItem *
416 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
418     if (move_to_bottom) {
419         sp_canvas_item_move_to_z(item, 0);
420     }
422     return temporary_item_list->add_item(item, lifetime);
425 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
426 */
427 void
428 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
430     // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
431     if (tempitem && temporary_item_list) {
432         temporary_item_list->delete_item(tempitem);
433     }
436 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
437     SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
438     canvas->rendermode = mode;
439     _display_mode = mode;
440     if (mode != Inkscape::RENDERMODE_OUTLINE) {
441         _saved_display_mode = _display_mode;
442     }
443     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
444     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
447 void SPDesktop::displayModeToggle() {
448     if (_display_mode == Inkscape::RENDERMODE_OUTLINE) {
449         _setDisplayMode(_saved_display_mode);
450     } else {
451         _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
452     }
455 /**
456  * Returns current root (=bottom) layer.
457  */
458 SPObject *SPDesktop::currentRoot() const
460     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
463 /**
464  * Returns current top layer.
465  */
466 SPObject *SPDesktop::currentLayer() const
468     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
471 /**
472  * Sets the current layer of the desktop.
473  *
474  * Make \a object the top layer.
475  */
476 void SPDesktop::setCurrentLayer(SPObject *object) {
477     g_return_if_fail(SP_IS_GROUP(object));
478     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
479     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
480     _layer_hierarchy->setBottom(object);
483 void SPDesktop::toggleLayerSolo(SPObject *object) {
484     g_return_if_fail(SP_IS_GROUP(object));
485     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
487     bool othersShowing = false;
488     std::vector<SPObject*> layers;
489     for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
490         layers.push_back(obj);
491         othersShowing |= !SP_ITEM(obj)->isHidden();
492     }
493     for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
494         layers.push_back(obj);
495         othersShowing |= !SP_ITEM(obj)->isHidden();
496     }
499     if ( SP_ITEM(object)->isHidden() ) {
500         SP_ITEM(object)->setHidden(false);
501     }
503     for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
504         SP_ITEM(*it)->setHidden(othersShowing);
505     }
508 /**
509  * Return layer that contains \a object.
510  */
511 SPObject *SPDesktop::layerForObject(SPObject *object) {
512     g_return_val_if_fail(object != NULL, NULL);
514     SPObject *root=currentRoot();
515     object = SP_OBJECT_PARENT(object);
516     while ( object && object != root && !isLayer(object) ) {
517         object = SP_OBJECT_PARENT(object);
518     }
519     return object;
522 /**
523  * True if object is a layer.
524  */
525 bool SPDesktop::isLayer(SPObject *object) const {
526     return ( SP_IS_GROUP(object)
527              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
528                   == SPGroup::LAYER ) );
531 /**
532  * True if desktop viewport fully contains \a item's bbox.
533  */
534 bool SPDesktop::isWithinViewport (SPItem *item) const
536     Geom::Rect const viewport = get_display_area();
537     Geom::OptRect const bbox = sp_item_bbox_desktop(item);
538     if (bbox) {
539         return viewport.contains(*bbox);
540     } else {
541         return true;
542     }
545 ///
546 bool SPDesktop::itemIsHidden(SPItem const *item) const {
547     return item->isHidden(this->dkey);
550 /**
551  * Set activate property of desktop; emit signal if changed.
552  */
553 void
554 SPDesktop::set_active (bool new_active)
556     if (new_active != _active) {
557         _active = new_active;
558         if (new_active) {
559             _activate_signal.emit();
560         } else {
561             _deactivate_signal.emit();
562         }
563     }
566 /**
567  * Set activate status of current desktop's named view.
568  */
569 void
570 SPDesktop::activate_guides(bool activate)
572     guides_active = activate;
573     namedview->activateGuides(this, activate);
576 /**
577  * Make desktop switch documents.
578  */
579 void
580 SPDesktop::change_document (SPDocument *theDocument)
582     g_return_if_fail (theDocument != NULL);
584     /* unselect everything before switching documents */
585     selection->clear();
587     setDocument (theDocument);
589     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
590        (this can probably be done in a better way) */
591     Gtk::Window *parent = this->getToplevel();
592     g_assert(parent != NULL);
593     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
594     if (dtw) dtw->desktop = this;
595     sp_desktop_widget_update_namedview(dtw);
597     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
598     _document_replaced_signal.emit (this, theDocument);
601 /**
602  * Make desktop switch event contexts.
603  */
604 void
605 SPDesktop::set_event_context (GtkType type, const gchar *config)
607     SPEventContext *ec;
608     while (event_context) {
609         ec = event_context;
610         sp_event_context_deactivate (ec);
611         event_context = ec->next;
612         sp_event_context_finish (ec);
613         g_object_unref (G_OBJECT (ec));
614     }
616     ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
617     ec->next = event_context;
618     event_context = ec;
619     sp_event_context_activate (ec);
620     _event_context_changed_signal.emit (this, ec);
623 /**
624  * Push event context onto desktop's context stack.
625  */
626 void
627 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
629     SPEventContext *ref, *ec;
631     if (event_context && event_context->key == key) return;
632     ref = event_context;
633     while (ref && ref->next && ref->next->key != key) ref = ref->next;
634     if (ref && ref->next) {
635         ec = ref->next;
636         ref->next = ec->next;
637         sp_event_context_finish (ec);
638         g_object_unref (G_OBJECT (ec));
639     }
641     if (event_context) sp_event_context_deactivate (event_context);
642     ec = sp_event_context_new (type, this, config, key);
643     ec->next = event_context;
644     event_context = ec;
645     sp_event_context_activate (ec);
646     _event_context_changed_signal.emit (this, ec);
649 /**
650  * Sets the coordinate status to a given point
651  */
652 void
653 SPDesktop::set_coordinate_status (Geom::Point p) {
654     _widget->setCoordinateStatus(p);
657 /**
658  * \see sp_document_item_from_list_at_point_bottom()
659  */
660 SPItem *
661 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
663     g_return_val_if_fail (doc() != NULL, NULL);
664     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
667 /**
668  * \see sp_document_item_at_point()
669  */
670 SPItem *
671 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
673     g_return_val_if_fail (doc() != NULL, NULL);
674     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
677 /**
678  * \see sp_document_group_at_point()
679  */
680 SPItem *
681 SPDesktop::group_at_point (Geom::Point const p) const
683     g_return_val_if_fail (doc() != NULL, NULL);
684     return sp_document_group_at_point (doc(), dkey, p);
687 /**
688  * \brief  Returns the mouse point in document coordinates; if mouse is
689  * outside the canvas, returns the center of canvas viewpoint
690  */
691 Geom::Point
692 SPDesktop::point() const
694     Geom::Point p = _widget->getPointer();
695     Geom::Point pw = sp_canvas_window_to_world (canvas, p);
696     p = w2d(pw);
698     Geom::Rect const r = canvas->getViewbox();
700     Geom::Point r0 = w2d(r.min());
701     Geom::Point r1 = w2d(r.max());
703     if (p[Geom::X] >= r0[Geom::X] &&
704         p[Geom::X] <= r1[Geom::X] &&
705         p[Geom::Y] >= r1[Geom::Y] &&
706         p[Geom::Y] <= r0[Geom::Y])
707     {
708         return p;
709     } else {
710         return (r0 + r1) / 2;
711     }
714 /**
715  * Put current zoom data in history list.
716  */
717 void
718 SPDesktop::push_current_zoom (GList **history)
720     Geom::Rect const area = get_display_area();
722     NRRect *old_zoom = g_new(NRRect, 1);
723     old_zoom->x0 = area.min()[Geom::X];
724     old_zoom->x1 = area.max()[Geom::X];
725     old_zoom->y0 = area.min()[Geom::Y];
726     old_zoom->y1 = area.max()[Geom::Y];
727     if ( *history == NULL
728          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
729                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
730                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
731                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
732     {
733         *history = g_list_prepend (*history, old_zoom);
734     }
737 /**
738  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
739  */
740 void
741 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
743     g_assert(_widget);
745     // save the zoom
746     if (log) {
747         push_current_zoom(&zooms_past);
748         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
749         g_list_free (zooms_future);
750         zooms_future = NULL;
751     }
753     double const cx = 0.5 * (x0 + x1);
754     double const cy = 0.5 * (y0 + y1);
756     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
757     Geom::Rect viewbox = canvas->getViewbox();
758     viewbox.expandBy(-border);
760     double scale = _d2w.descrim();
761     double newscale;
762     if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
763         newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
764     } else {
765         newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
766     }
768     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
770     int clear = FALSE;
771     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
772         /* Set zoom factors */
773         _d2w = Geom::Scale(newscale, -newscale);
774         _w2d = Geom::Scale(1/newscale, 1/-newscale);
775         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
776         clear = TRUE;
777     }
779     /* Calculate top left corner (in document pixels) */
780     x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
781     y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
783     /* Scroll */
784     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
786     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
787     sp_box3d_context_update_lines(event_context);
789     _widget->updateRulers();
790     _widget->updateScrollbars(_d2w.descrim());
791     _widget->updateZoom();
794 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
796     set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
799 /**
800  * Return viewbox dimensions.
801  */
802 Geom::Rect SPDesktop::get_display_area() const
804     Geom::Rect const viewbox = canvas->getViewbox();
806     double const scale = _d2w[0];
808     return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
809                       Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
812 /**
813  * Revert back to previous zoom if possible.
814  */
815 void
816 SPDesktop::prev_zoom()
818     if (zooms_past == NULL) {
819         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
820         return;
821     }
823     // push current zoom into forward zooms list
824     push_current_zoom (&zooms_future);
826     // restore previous zoom
827     set_display_area (((NRRect *) zooms_past->data)->x0,
828             ((NRRect *) zooms_past->data)->y0,
829             ((NRRect *) zooms_past->data)->x1,
830             ((NRRect *) zooms_past->data)->y1,
831             0, false);
833     // remove the just-added zoom from the past zooms list
834     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
837 /**
838  * Set zoom to next in list.
839  */
840 void
841 SPDesktop::next_zoom()
843     if (zooms_future == NULL) {
844         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
845         return;
846     }
848     // push current zoom into past zooms list
849     push_current_zoom (&zooms_past);
851     // restore next zoom
852     set_display_area (((NRRect *) zooms_future->data)->x0,
853             ((NRRect *) zooms_future->data)->y0,
854             ((NRRect *) zooms_future->data)->x1,
855             ((NRRect *) zooms_future->data)->y1,
856             0, false);
858     // remove the just-used zoom from the zooms_future list
859     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
862 #include "tools-switch.h"
863 #include "node-context.h"
864 #include "shape-editor.h"
865 #include "nodepath.h"
867 /** \brief  Performs a quick zoom into what the user is working on
868     \param  enable  Whether we're going in or out of quick zoom
870 */
871 void
872 SPDesktop::zoom_quick (bool enable)
874     if (enable == _quick_zoom_enabled) {
875         return;
876     }
878     if (enable == true) {
879         _quick_zoom_stored_area = get_display_area();
880         bool zoomed = false;
882         if (!zoomed) {
883             SPItem * singleItem = selection->singleItem();
884             if (singleItem != NULL && tools_isactive(this, TOOLS_NODES)) {
886                 Inkscape::NodePath::Path * nodepath = event_context->shape_editor->get_nodepath();
887                 // printf("I've got a nodepath, crazy\n");
889                 Geom::Rect nodes;
890                 bool firstnode = true;
892                 if (nodepath->selected) {
893                     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
894                        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
895                         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
896                            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
897                             if (node->selected) {
898                                 // printf("\tSelected node\n");
899                                 if (firstnode) {
900                                     nodes = Geom::Rect(node->pos, node->pos);
901                                     firstnode = false;
902                                 } else {
903                                     nodes.expandTo(node->pos);
904                                 }
906                                 if (node->p.other != NULL) {
907                                     /* Include previous node pos */
908                                     nodes.expandTo(node->p.other->pos);
910                                     /* Include previous handle */
911                                     if (!sp_node_side_is_line(node, &node->p)) {
912                                         nodes.expandTo(node->p.pos);
913                                     }
914                                 }
916                                 if (node->n.other != NULL) {
917                                     /* Include previous node pos */
918                                     nodes.expandTo(node->n.other->pos);
920                                     /* Include previous handle */
921                                     if (!sp_node_side_is_line(node, &node->n)) {
922                                         nodes.expandTo(node->n.pos);
923                                     }
924                                 }
925                             }
926                         }
927                     }
929                     if (!firstnode && nodes.area() * 2.0 < _quick_zoom_stored_area.area()) {
930                         set_display_area(nodes, 10);
931                         zoomed = true;
932                     }
933                 }
934             }
935         }
937         if (!zoomed) {
938             Geom::OptRect const d = selection->bounds();
939             if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
940                 set_display_area(*d, 10);
941                 zoomed = true;
942             }
943         }
945         if (!zoomed) {
946             zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
947             zoomed = true;
948         }
949     } else {
950         set_display_area(_quick_zoom_stored_area, 0);
951     }
953     _quick_zoom_enabled = enable;
954     return;
957 /**
958  * Zoom to point with absolute zoom factor.
959  */
960 void
961 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
963     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
965     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
966     // this check prevents "sliding" when trying to zoom in at maximum zoom;
967     /// \todo someone please fix calculations properly and remove this hack
968     if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
969         return;
971     Geom::Rect const viewbox = canvas->getViewbox();
973     double const width2 = viewbox.dimensions()[Geom::X] / zoom;
974     double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
976     set_display_area(cx - px * width2,
977                      cy - py * height2,
978                      cx + (1 - px) * width2,
979                      cy + (1 - py) * height2,
980                      0.0);
983 /**
984  * Zoom to center with absolute zoom factor.
985  */
986 void
987 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
989     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
992 /**
993  * Zoom to point with relative zoom factor.
994  */
995 void
996 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
998     Geom::Rect const area = get_display_area();
1000     if (cx < area.min()[Geom::X]) {
1001         cx = area.min()[Geom::X];
1002     }
1003     if (cx > area.max()[Geom::X]) {
1004         cx = area.max()[Geom::X];
1005     }
1006     if (cy < area.min()[Geom::Y]) {
1007         cy = area.min()[Geom::Y];
1008     }
1009     if (cy > area.max()[Geom::Y]) {
1010         cy = area.max()[Geom::Y];
1011     }
1013     gdouble const scale = _d2w.descrim() * zoom;
1014     double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
1015     double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
1017     zoom_absolute_keep_point(cx, cy, px, py, scale);
1020 /**
1021  * Zoom to center with relative zoom factor.
1022  */
1023 void
1024 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1026     gdouble scale = _d2w.descrim() * zoom;
1027     zoom_absolute (cx, cy, scale);
1030 /**
1031  * Set display area to origin and current document dimensions.
1032  */
1033 void
1034 SPDesktop::zoom_page()
1036     Geom::Rect d(Geom::Point(0, 0),
1037                  Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1039     if (d.minExtent() < 1.0) {
1040         return;
1041     }
1043     set_display_area(d, 10);
1046 /**
1047  * Set display area to current document width.
1048  */
1049 void
1050 SPDesktop::zoom_page_width()
1052     Geom::Rect const a = get_display_area();
1054     if (sp_document_width(doc()) < 1.0) {
1055         return;
1056     }
1058     Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1059                  Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1061     set_display_area(d, 10);
1064 /**
1065  * Zoom to selection.
1066  */
1067 void
1068 SPDesktop::zoom_selection()
1070     Geom::OptRect const d = selection->bounds();
1072     if ( !d || d->minExtent() < 0.1 ) {
1073         return;
1074     }
1076     set_display_area(*d, 10);
1079 /**
1080  * Tell widget to let zoom widget grab keyboard focus.
1081  */
1082 void
1083 SPDesktop::zoom_grab_focus()
1085     _widget->letZoomGrabFocus();
1088 /**
1089  * Zoom to whole drawing.
1090  */
1091 void
1092 SPDesktop::zoom_drawing()
1094     g_return_if_fail (doc() != NULL);
1095     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1096     g_return_if_fail (docitem != NULL);
1098     Geom::OptRect d = sp_item_bbox_desktop(docitem);
1100     /* Note that the second condition here indicates that
1101     ** there are no items in the drawing.
1102     */
1103     if ( !d || d->minExtent() < 0.1 ) {
1104         return;
1105     }
1107     set_display_area(*d, 10);
1110 /**
1111  * Scroll canvas by specific coordinate amount in svg coordinates.
1112  */
1113 void
1114 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1116     double scale = _d2w.descrim();
1117     scroll_world(dx*scale, dy*scale, is_scrolling);
1120 /**
1121  * Scroll canvas by specific coordinate amount.
1122  */
1123 void
1124 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1126     g_assert(_widget);
1128     Geom::Rect const viewbox = canvas->getViewbox();
1130     sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1132     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1133     sp_box3d_context_update_lines(event_context);
1135     _widget->updateRulers();
1136     _widget->updateScrollbars(_d2w.descrim());
1139 bool
1140 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1142     using Geom::X;
1143     using Geom::Y;
1145     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1146     gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1148     // autoscrolldistance is in screen pixels, but the display area is in document units
1149     autoscrolldistance /= _d2w.descrim();
1150     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1151     Geom::Rect dbox = get_display_area();
1152     dbox.expandBy(-autoscrolldistance);
1154     if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1155         !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y])   ) {
1157         Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1159         gdouble x_to;
1160         if (p[X] < dbox.min()[X])
1161             x_to = dbox.min()[X];
1162         else if (p[X] > dbox.max()[X])
1163             x_to = dbox.max()[X];
1164         else
1165             x_to = p[X];
1167         gdouble y_to;
1168         if (p[Y] < dbox.min()[Y])
1169             y_to = dbox.min()[Y];
1170         else if (p[Y] > dbox.max()[Y])
1171             y_to = dbox.max()[Y];
1172         else
1173             y_to = p[Y];
1175         Geom::Point const d_dt(x_to, y_to);
1176         Geom::Point const d_w( d_dt * _d2w );
1177         Geom::Point const moved_w( d_w - s_w );
1179         if (autoscrollspeed == 0)
1180             autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1182         if (autoscrollspeed != 0)
1183             scroll_world (autoscrollspeed * moved_w);
1185         return true;
1186     }
1187     return false;
1190 bool
1191 SPDesktop::is_iconified()
1193     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1196 void
1197 SPDesktop::iconify()
1199     _widget->setIconified();
1202 bool
1203 SPDesktop::is_maximized()
1205     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1208 void
1209 SPDesktop::maximize()
1211     _widget->setMaximized();
1214 bool
1215 SPDesktop::is_fullscreen()
1217     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1220 void
1221 SPDesktop::fullscreen()
1223     _widget->setFullscreen();
1226 /** \brief  Checks to see if the user is working in focused mode
1228     Returns the value of \c _focusMode
1229 */
1230 bool
1231 SPDesktop::is_focusMode()
1233     return _focusMode;
1236 /** \brief  Changes whether the user is in focus mode or not
1237     \param  mode  Which mode the view should be in
1239 */
1240 void
1241 SPDesktop::focusMode (bool mode)
1243     if (mode == _focusMode) { return; }
1245     _focusMode = mode;
1247     layoutWidget();
1248     //sp_desktop_widget_layout(SPDesktopWidget);
1250     return;
1253 void
1254 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1256     _widget->getGeometry (x, y, w, h);
1259 void
1260 SPDesktop::setWindowPosition (Geom::Point p)
1262     _widget->setPosition (p);
1265 void
1266 SPDesktop::setWindowSize (gint w, gint h)
1268     _widget->setSize (w, h);
1271 void
1272 SPDesktop::setWindowTransient (void *p, int transient_policy)
1274     _widget->setTransient (p, transient_policy);
1277 Gtk::Window*
1278 SPDesktop::getToplevel( )
1280     return _widget->getWindow();
1283 void
1284 SPDesktop::presentWindow()
1286     _widget->present();
1289 bool
1290 SPDesktop::warnDialog (gchar *text)
1292     return _widget->warnDialog (text);
1295 void
1296 SPDesktop::toggleRulers()
1298     _widget->toggleRulers();
1301 void
1302 SPDesktop::toggleScrollbars()
1304     _widget->toggleScrollbars();
1307 void
1308 SPDesktop::layoutWidget()
1310     _widget->layout();
1313 void
1314 SPDesktop::destroyWidget()
1316     _widget->destroy();
1319 bool
1320 SPDesktop::shutdown()
1322     return _widget->shutdown();
1325 bool SPDesktop::onDeleteUI (GdkEventAny*)
1327     if(shutdown())
1328         return true;
1330     destroyWidget();
1331     return false;
1334 /**
1335  *  onWindowStateEvent
1336  *
1337  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1338  *  Since GTK doesn't have a way to query this state information directly, we
1339  *  record it for the desktop here, and also possibly trigger a layout.
1340  */
1341 bool
1342 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1344     // Record the desktop window's state
1345     window_state = event->new_window_state;
1347     // Layout may differ depending on full-screen mode or not
1348     GdkWindowState changed = event->changed_mask;
1349     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1350         layoutWidget();
1351     }
1353     return false;
1356 void
1357 SPDesktop::setToolboxFocusTo (gchar const *label)
1359     _widget->setToolboxFocusTo (label);
1362 void
1363 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1365     _widget->setToolboxAdjustmentValue (id, val);
1368 void
1369 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1371     _widget->setToolboxSelectOneValue (id, val);
1374 bool
1375 SPDesktop::isToolboxButtonActive (gchar const *id)
1377     return _widget->isToolboxButtonActive (id);
1380 void
1381 SPDesktop::emitToolSubselectionChanged(gpointer data)
1383     _tool_subselection_changed.emit(data);
1384     inkscape_subselection_changed (this);
1387 void
1388 SPDesktop::updateNow()
1390   sp_canvas_update_now(canvas);
1393 void
1394 SPDesktop::enableInteraction()
1396   _widget->enableInteraction();
1399 void SPDesktop::disableInteraction()
1401   _widget->disableInteraction();
1404 void SPDesktop::setWaitingCursor()
1406     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1407     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1408     gdk_cursor_unref(waiting);
1409     // GDK needs the flush for the cursor change to take effect
1410     gdk_flush();
1411     waiting_cursor = true;
1414 void SPDesktop::clearWaitingCursor()
1416   if (waiting_cursor)
1417       sp_event_context_update_cursor(sp_desktop_event_context(this));
1420 void SPDesktop::toggleColorProfAdjust()
1422     _widget->toggleColorProfAdjust();
1425 void SPDesktop::toggleGrids()
1427     if (namedview->grids) {
1428         if(gridgroup) {
1429             showGrids(!grids_visible);
1430         }
1431     } else {
1432         //there is no grid present at the moment. add a rectangular grid and make it visible
1433         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1434         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1435         showGrids(true);
1436     }
1439 void SPDesktop::showGrids(bool show, bool dirty_document)
1441     grids_visible = show;
1442     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1443     if (show) {
1444         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1445     } else {
1446         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1447     }
1450 void SPDesktop::toggleSnapGlobal()
1452     bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1453     Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1454     sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1457 //----------------------------------------------------------------------
1458 // Callback implementations. The virtual ones are connected by the view.
1460 void
1461 SPDesktop::onPositionSet (double x, double y)
1463     _widget->viewSetPosition (Geom::Point(x,y));
1466 void
1467 SPDesktop::onResized (double /*x*/, double /*y*/)
1469    // Nothing called here
1472 /**
1473  * Redraw callback; queues Gtk redraw; connected by View.
1474  */
1475 void
1476 SPDesktop::onRedrawRequested ()
1478     if (main) {
1479         _widget->requestCanvasUpdate();
1480     }
1483 void
1484 SPDesktop::updateCanvasNow()
1486   _widget->requestCanvasUpdateAndWait();
1489 /**
1490  * Associate document with desktop.
1491  */
1492 void
1493 SPDesktop::setDocument (SPDocument *doc)
1495     if (this->doc() && doc) {
1496         namedview->hide(this);
1497         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1498     }
1500     if (_layer_hierarchy) {
1501         _layer_hierarchy->clear();
1502         delete _layer_hierarchy;
1503     }
1504     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1505     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1506     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1507     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1508     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1510     /* setup EventLog */
1511     event_log = new Inkscape::EventLog(doc);
1512     doc->addUndoObserver(*event_log);
1514     _commit_connection.disconnect();
1515     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1517     /// \todo fixme: This condition exists to make sure the code
1518     /// inside is NOT called on initialization, only on replacement. But there
1519     /// are surely more safe methods to accomplish this.
1520     // TODO since the comment had reversed logic, check the intent of this block of code:
1521     if (drawing) {
1522         NRArenaItem *ai = 0;
1524         namedview = sp_document_namedview (doc, NULL);
1525         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1526         number = namedview->getViewCount();
1528         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1529                 SP_CANVAS_ARENA (drawing)->arena,
1530                 dkey,
1531                 SP_ITEM_SHOW_DISPLAY);
1532         if (ai) {
1533             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1534         }
1535         namedview->show(this);
1536         /* Ugly hack */
1537         activate_guides (true);
1538         /* Ugly hack */
1539         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1540     }
1542     _document_replaced_signal.emit (this, doc);
1544     View::setDocument (doc);
1547 void
1548 SPDesktop::onStatusMessage
1549 (Inkscape::MessageType type, gchar const *message)
1551     if (_widget) {
1552         _widget->setMessage(type, message);
1553     }
1556 void
1557 SPDesktop::onDocumentURISet (gchar const* uri)
1559     _widget->setTitle(uri);
1562 /**
1563  * Resized callback.
1564  */
1565 void
1566 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1568     _doc2dt[5] = height;
1569     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1570     Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1571     SP_CTRLRECT(page)->setRectangle(a);
1572     SP_CTRLRECT(page_border)->setRectangle(a);
1576 void
1577 SPDesktop::_onActivate (SPDesktop* dt)
1579     if (!dt->_widget) return;
1580     dt->_widget->activateDesktop();
1583 void
1584 SPDesktop::_onDeactivate (SPDesktop* dt)
1586     if (!dt->_widget) return;
1587     dt->_widget->deactivateDesktop();
1590 void
1591 SPDesktop::_onSelectionModified
1592 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1594     if (!dt->_widget) return;
1595     dt->_widget->updateScrollbars (dt->_d2w.descrim());
1598 static void
1599 _onSelectionChanged
1600 (Inkscape::Selection *selection, SPDesktop *desktop)
1602     /** \todo
1603      * only change the layer for single selections, or what?
1604      * This seems reasonable -- for multiple selections there can be many
1605      * different layers involved.
1606      */
1607     SPItem *item=selection->singleItem();
1608     if (item) {
1609         SPObject *layer=desktop->layerForObject(item);
1610         if ( layer && layer != desktop->currentLayer() ) {
1611             desktop->setCurrentLayer(layer);
1612         }
1613     }
1616 /**
1617  * Calls event handler of current event context.
1618  * \param arena Unused
1619  * \todo fixme
1620  */
1621 static gint
1622 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1624     if (ai) {
1625         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1626         return sp_event_context_item_handler (desktop->event_context, spi, event);
1627     } else {
1628         return sp_event_context_root_handler (desktop->event_context, event);
1629     }
1632 static void
1633 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1634     g_return_if_fail(SP_IS_GROUP(layer));
1635     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1638 /// Callback
1639 static void
1640 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1641     g_return_if_fail(SP_IS_GROUP(layer));
1642     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1645 /// Callback
1646 static void
1647 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1648                                          SPDesktop *desktop)
1650     desktop->_layer_changed_signal.emit (bottom);
1653 /// Called when document is starting to be rebuilt.
1654 static void
1655 _reconstruction_start (SPDesktop * desktop)
1657     // printf("Desktop, starting reconstruction\n");
1658     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1659     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1661     /*
1662     GSList const * selection_objs = desktop->selection->list();
1663     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1665     }
1666     */
1667     desktop->selection->clear();
1669     // printf("Desktop, starting reconstruction end\n");
1672 /// Called when document rebuild is finished.
1673 static void
1674 _reconstruction_finish (SPDesktop * desktop)
1676     // printf("Desktop, finishing reconstruction\n");
1677     if (desktop->_reconstruction_old_layer_id == NULL)
1678         return;
1680     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1681     if (newLayer != NULL)
1682         desktop->setCurrentLayer(newLayer);
1684     g_free(desktop->_reconstruction_old_layer_id);
1685     desktop->_reconstruction_old_layer_id = NULL;
1686     // printf("Desktop, finishing reconstruction end\n");
1687     return;
1690 /**
1691  * Namedview_modified callback.
1692  */
1693 static void
1694 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1696     SPNamedView *nv=SP_NAMEDVIEW(obj);
1698     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1700         /* Show/hide page background */
1701         if (nv->pagecolor & 0xff) {
1702             sp_canvas_item_show (desktop->table);
1703             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1704             sp_canvas_item_move_to_z (desktop->table, 0);
1705         } else {
1706             sp_canvas_item_hide (desktop->table);
1707         }
1709         /* Show/hide page border */
1710         if (nv->showborder) {
1711             // show
1712             sp_canvas_item_show (desktop->page_border);
1713             // set color and shadow
1714             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1715             if (nv->pageshadow) {
1716                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1717             }
1718             // place in the z-order stack
1719             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1720                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1721             } else {
1722                 int order = sp_canvas_item_order (desktop->page_border);
1723                 int morder = sp_canvas_item_order (desktop->drawing);
1724                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1725                     morder - order);
1726             }
1727         } else {
1728                 sp_canvas_item_hide (desktop->page_border);
1729                 if (nv->pageshadow) {
1730                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1731                 }
1732         }
1734         /* Show/hide page shadow */
1735         if (nv->showpageshadow && nv->pageshadow) {
1736             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1737         } else {
1738             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1739         }
1741         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1742         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1743             (SP_RGBA32_R_U(nv->pagecolor) +
1744              SP_RGBA32_G_U(nv->pagecolor) +
1745              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1746             // the background color is light or transparent, use black outline
1747             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1748         } else { // use white outline
1749             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1750         }
1751     }
1754 Geom::Matrix SPDesktop::w2d() const
1756     return _w2d;
1759 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1761     return p * _w2d;
1764 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1766     return p * _d2w;
1769 Geom::Matrix SPDesktop::doc2dt() const
1771     return _doc2dt;
1774 Geom::Matrix SPDesktop::dt2doc() const
1776     // doc2dt is its own inverse
1777     return _doc2dt;
1780 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1782     return p * _doc2dt;
1785 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1787     return p * dt2doc();
1791 /**
1792  * Pop event context from desktop's context stack. Never used.
1793  */
1794 // void
1795 // SPDesktop::pop_event_context (unsigned int key)
1796 // {
1797 //    SPEventContext *ec = NULL;
1798 //
1799 //    if (event_context && event_context->key == key) {
1800 //        g_return_if_fail (event_context);
1801 //        g_return_if_fail (event_context->next);
1802 //        ec = event_context;
1803 //        sp_event_context_deactivate (ec);
1804 //        event_context = ec->next;
1805 //        sp_event_context_activate (event_context);
1806 //        _event_context_changed_signal.emit (this, ec);
1807 //    }
1808 //
1809 //    SPEventContext *ref = event_context;
1810 //    while (ref && ref->next && ref->next->key != key)
1811 //        ref = ref->next;
1812 //
1813 //    if (ref && ref->next) {
1814 //        ec = ref->next;
1815 //        ref->next = ec->next;
1816 //    }
1817 //
1818 //    if (ec) {
1819 //        sp_event_context_finish (ec);
1820 //        g_object_unref (G_OBJECT (ec));
1821 //    }
1822 // }
1824 /*
1825   Local Variables:
1826   mode:c++
1827   c-file-style:"stroustrup"
1828   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1829   indent-tabs-mode:nil
1830   fill-column:99
1831   End:
1832 */
1833 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :