Code

From trunk
[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);
107 static void _update_snap_distances (SPDesktop *desktop);
109 /**
110  * Return new desktop object.
111  * \pre namedview != NULL.
112  * \pre canvas != NULL.
113  */
114 SPDesktop::SPDesktop() :
115     _dlg_mgr( 0 ),
116     namedview( 0 ),
117     canvas( 0 ),
118     selection( 0 ),
119     event_context( 0 ),
120     layer_manager( 0 ),
121     event_log( 0 ),
122     temporary_item_list( 0 ),
123     snapindicator( 0 ),
124     acetate( 0 ),
125     main( 0 ),
126     gridgroup( 0 ),
127     guides( 0 ),
128     drawing( 0 ),
129     sketch( 0 ),
130     controls( 0 ),
131     tempgroup ( 0 ),
132     table( 0 ),
133     page( 0 ),
134     page_border( 0 ),
135     current( 0 ),
136     _focusMode(false),
137     zooms_past( 0 ),
138     zooms_future( 0 ),
139     dkey( 0 ),
140     number( 0 ),
141     window_state(0),
142     interaction_disabled_counter( 0 ),
143     waiting_cursor( false ),
144     guides_active( false ),
145     gr_item( 0 ),
146     gr_point_type( 0 ),
147     gr_point_i( 0 ),
148     gr_fill_or_stroke( true ),
149     _layer_hierarchy( 0 ),
150     _reconstruction_old_layer_id( 0 ),
151     _display_mode(Inkscape::RENDERMODE_NORMAL),
152     _saved_display_mode(Inkscape::RENDERMODE_NORMAL),
153     _widget( 0 ),
154     _inkscape( 0 ),
155     _guides_message_context( 0 ),
156     _active( false ),
157     _w2d(),
158     _d2w(),
159     _doc2dt( Geom::Scale(1, -1) ),
160     grids_visible( false )
162     _d2w.setIdentity();
163     _w2d.setIdentity();
165     selection = Inkscape::GC::release( new Inkscape::Selection(this) );
168 void
169 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
171     // Temporary workaround for link order issues:
172     Inkscape::DeviceManager::getManager().getDevices();
173     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
175     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
177     current = prefs->getStyle("/desktop/style");
179     namedview = nv;
180     canvas = aCanvas;
182     SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
183     /* Kill flicker */
184     sp_document_ensure_up_to_date (document);
186     /* Setup Dialog Manager */
187     _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
189     dkey = sp_item_display_key_new (1);
191     /* Connect document */
192     setDocument (document);
194     number = namedview->getViewCount();
197     /* Setup Canvas */
198     g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
200     SPCanvasGroup *root = sp_canvas_root (canvas);
202     /* Setup adminstrative layers */
203     acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
204     g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
205     main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
206     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
208     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
209     SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
210     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
211     sp_canvas_item_move_to_z (table, 0);
213     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
214     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
215     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
217     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
218     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
220     SP_CANVAS_ARENA (drawing)->arena->delta = prefs->getDouble("/options/cursortolerance/value", 1.0); // default is 1 px
222     if (prefs->getBool("/options/startmode/outline")) {
223         // Start in outline mode
224         setDisplayModeOutline();
225     } else {
226         // Start in normal mode, default
227         setDisplayModeNormal();
228     }
230     gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
231     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
232     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
233     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
234     tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
236     /* Push select tool to the bottom of stack */
237     /** \todo
238      * FIXME: this is the only call to this.  Everything else seems to just
239      * call "set" instead of "push".  Can we assume that there is only one
240      * context ever?
241      */
242     push_event_context (SP_TYPE_SELECT_CONTEXT, "/tools/select", SP_EVENT_CONTEXT_STATIC);
244     // display rect and zoom are now handled in sp_desktop_widget_realize()
246     Geom::Rect const d(Geom::Point(0.0, 0.0),
247                        Geom::Point(sp_document_width(document), sp_document_height(document)));
249     SP_CTRLRECT(page)->setRectangle(d);
250     SP_CTRLRECT(page_border)->setRectangle(d);
252     /* the following sets the page shadow on the canvas
253        It was originally set to 5, which is really cheesy!
254        It now is an attribute in the document's namedview. If a value of
255        0 is used, then the constructor for a shadow is not initialized.
256     */
258     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
259         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
260     }
263     /* Connect event for page resize */
264     _doc2dt[5] = sp_document_height (document);
265     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
267     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
269     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
270             SP_CANVAS_ARENA (drawing)->arena,
271             dkey,
272             SP_ITEM_SHOW_DISPLAY);
273     if (ai) {
274         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
275     }
277     namedview->show(this);
278     /* Ugly hack */
279     activate_guides (true);
280     /* Ugly hack */
281     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
283 /* Set up notification of rebuilding the document, this allows
284        for saving object related settings in the document. */
285     _reconstruction_start_connection =
286         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
287     _reconstruction_finish_connection =
288         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
289     _reconstruction_old_layer_id = NULL;
291     // ?
292     // sp_active_desktop_set (desktop);
293     _inkscape = INKSCAPE;
295     _activate_connection = _activate_signal.connect(
296         sigc::bind(
297             sigc::ptr_fun(_onActivate),
298             this
299         )
300     );
301      _deactivate_connection = _deactivate_signal.connect(
302         sigc::bind(
303             sigc::ptr_fun(_onDeactivate),
304             this
305         )
306     );
308     _sel_modified_connection = selection->connectModified(
309         sigc::bind(
310             sigc::ptr_fun(&_onSelectionModified),
311             this
312         )
313     );
314     _sel_changed_connection = selection->connectChanged(
315         sigc::bind(
316             sigc::ptr_fun(&_onSelectionChanged),
317             this
318         )
319     );
322     /* setup LayerManager */
323     //   (Setting up after the connections are all in place, as it may use some of them)
324     layer_manager = new Inkscape::LayerManager( this );
326     showGrids(namedview->grids_visible, false);
328     temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
329     snapindicator = new Inkscape::Display::SnapIndicator ( this );
333 void SPDesktop::destroy()
335     if (snapindicator) {
336         delete snapindicator;
337         snapindicator = NULL;
338     }
339     if (temporary_item_list) {
340         delete temporary_item_list;
341         temporary_item_list = NULL;
342     }
344     if (selection) {
345         delete selection;
346         selection = NULL;
347     }
349     namedview->hide(this);
351     _activate_connection.disconnect();
352     _deactivate_connection.disconnect();
353     _sel_modified_connection.disconnect();
354     _sel_changed_connection.disconnect();
355     _modified_connection.disconnect();
356     _commit_connection.disconnect();
357     _reconstruction_start_connection.disconnect();
358     _reconstruction_finish_connection.disconnect();
360     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
361     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
362     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
364     while (event_context) {
365         SPEventContext *ec = event_context;
366         event_context = ec->next;
367         sp_event_context_finish (ec);
368         g_object_unref (G_OBJECT (ec));
369     }
371     if (_layer_hierarchy) {
372         delete _layer_hierarchy;
373 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
374     }
376     if (layer_manager) {
377         delete layer_manager;
378         layer_manager = NULL;
379     }
381     if (_inkscape) {
382         _inkscape = NULL;
383     }
385     if (drawing) {
386         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
387         drawing = NULL;
388     }
390     delete _guides_message_context;
391     _guides_message_context = NULL;
393     g_list_free (zooms_past);
394     g_list_free (zooms_future);
397 SPDesktop::~SPDesktop() {}
399 //--------------------------------------------------------------------
400 /* Public methods */
403 /* These methods help for temporarily showing things on-canvas.
404  * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
405  * is when you want to prematurely remove the item from the canvas, by calling
406  * desktop->remove_temporary_canvasitem(tempitem).
407  */
408 /** Note that lifetime is measured in milliseconds
409  * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
410  * delete the object for you and the reference will become invalid without you knowing it.
411  * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
412  * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
413  * because the object might be deleted already without you knowing it.
414  * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
415  */
416 Inkscape::Display::TemporaryItem *
417 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
419     if (move_to_bottom) {
420         sp_canvas_item_move_to_z(item, 0);
421     }
423     return temporary_item_list->add_item(item, lifetime);
426 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
427 */
428 void
429 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
431     // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
432     if (tempitem && temporary_item_list) {
433         temporary_item_list->delete_item(tempitem);
434     }
437 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
438     SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
439     canvas->rendermode = mode;
440     _display_mode = mode;
441     if (mode != Inkscape::RENDERMODE_OUTLINE) {
442         _saved_display_mode = _display_mode;
443     }
444     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
445     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
448 void SPDesktop::displayModeToggle() {
449     if (_display_mode == Inkscape::RENDERMODE_OUTLINE) {
450         _setDisplayMode(_saved_display_mode);
451     } else {
452         _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
453     }
456 /**
457  * Returns current root (=bottom) layer.
458  */
459 SPObject *SPDesktop::currentRoot() const
461     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
464 /**
465  * Returns current top layer.
466  */
467 SPObject *SPDesktop::currentLayer() const
469     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
472 /**
473  * Sets the current layer of the desktop.
474  *
475  * Make \a object the top layer.
476  */
477 void SPDesktop::setCurrentLayer(SPObject *object) {
478     g_return_if_fail(SP_IS_GROUP(object));
479     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
480     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
481     _layer_hierarchy->setBottom(object);
484 void SPDesktop::toggleLayerSolo(SPObject *object) {
485     g_return_if_fail(SP_IS_GROUP(object));
486     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
488     bool othersShowing = false;
489     std::vector<SPObject*> layers;
490     for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
491         layers.push_back(obj);
492         othersShowing |= !SP_ITEM(obj)->isHidden();
493     }
494     for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
495         layers.push_back(obj);
496         othersShowing |= !SP_ITEM(obj)->isHidden();
497     }
500     if ( SP_ITEM(object)->isHidden() ) {
501         SP_ITEM(object)->setHidden(false);
502     }
504     for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
505         SP_ITEM(*it)->setHidden(othersShowing);
506     }
509 /**
510  * Return layer that contains \a object.
511  */
512 SPObject *SPDesktop::layerForObject(SPObject *object) {
513     g_return_val_if_fail(object != NULL, NULL);
515     SPObject *root=currentRoot();
516     object = SP_OBJECT_PARENT(object);
517     while ( object && object != root && !isLayer(object) ) {
518         object = SP_OBJECT_PARENT(object);
519     }
520     return object;
523 /**
524  * True if object is a layer.
525  */
526 bool SPDesktop::isLayer(SPObject *object) const {
527     return ( SP_IS_GROUP(object)
528              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
529                   == SPGroup::LAYER ) );
532 /**
533  * True if desktop viewport fully contains \a item's bbox.
534  */
535 bool SPDesktop::isWithinViewport (SPItem *item) const
537     Geom::Rect const viewport = get_display_area();
538     boost::optional<Geom::Rect> const bbox = sp_item_bbox_desktop(item);
539     if (bbox) {
540         return viewport.contains(*bbox);
541     } else {
542         return true;
543     }
546 ///
547 bool SPDesktop::itemIsHidden(SPItem const *item) const {
548     return item->isHidden(this->dkey);
551 /**
552  * Set activate property of desktop; emit signal if changed.
553  */
554 void
555 SPDesktop::set_active (bool new_active)
557     if (new_active != _active) {
558         _active = new_active;
559         if (new_active) {
560             _activate_signal.emit();
561         } else {
562             _deactivate_signal.emit();
563         }
564     }
567 /**
568  * Set activate status of current desktop's named view.
569  */
570 void
571 SPDesktop::activate_guides(bool activate)
573     guides_active = activate;
574     namedview->activateGuides(this, activate);
577 /**
578  * Make desktop switch documents.
579  */
580 void
581 SPDesktop::change_document (SPDocument *theDocument)
583     g_return_if_fail (theDocument != NULL);
585     /* unselect everything before switching documents */
586     selection->clear();
588     setDocument (theDocument);
590     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
591        (this can probably be done in a better way) */
592     Gtk::Window *parent = this->getToplevel();
593     g_assert(parent != NULL);
594     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
595     if (dtw) dtw->desktop = this;
596     sp_desktop_widget_update_namedview(dtw);
598     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
599     _document_replaced_signal.emit (this, theDocument);
602 /**
603  * Make desktop switch event contexts.
604  */
605 void
606 SPDesktop::set_event_context (GtkType type, const gchar *config)
608     SPEventContext *ec;
609     while (event_context) {
610         ec = event_context;
611         sp_event_context_deactivate (ec);
612         event_context = ec->next;
613         sp_event_context_finish (ec);
614         g_object_unref (G_OBJECT (ec));
615     }
617     ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
618     ec->next = event_context;
619     event_context = ec;
620     sp_event_context_activate (ec);
621     _event_context_changed_signal.emit (this, ec);
624 /**
625  * Push event context onto desktop's context stack.
626  */
627 void
628 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
630     SPEventContext *ref, *ec;
632     if (event_context && event_context->key == key) return;
633     ref = event_context;
634     while (ref && ref->next && ref->next->key != key) ref = ref->next;
635     if (ref && ref->next) {
636         ec = ref->next;
637         ref->next = ec->next;
638         sp_event_context_finish (ec);
639         g_object_unref (G_OBJECT (ec));
640     }
642     if (event_context) sp_event_context_deactivate (event_context);
643     ec = sp_event_context_new (type, this, config, key);
644     ec->next = event_context;
645     event_context = ec;
646     sp_event_context_activate (ec);
647     _event_context_changed_signal.emit (this, ec);
650 /**
651  * Sets the coordinate status to a given point
652  */
653 void
654 SPDesktop::set_coordinate_status (Geom::Point p) {
655     _widget->setCoordinateStatus(p);
658 /**
659  * \see sp_document_item_from_list_at_point_bottom()
660  */
661 SPItem *
662 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
664     g_return_val_if_fail (doc() != NULL, NULL);
665     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
668 /**
669  * \see sp_document_item_at_point()
670  */
671 SPItem *
672 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
674     g_return_val_if_fail (doc() != NULL, NULL);
675     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
678 /**
679  * \see sp_document_group_at_point()
680  */
681 SPItem *
682 SPDesktop::group_at_point (Geom::Point const p) const
684     g_return_val_if_fail (doc() != NULL, NULL);
685     return sp_document_group_at_point (doc(), dkey, p);
688 /**
689  * \brief  Returns the mouse point in document coordinates; if mouse is
690  * outside the canvas, returns the center of canvas viewpoint
691  */
692 Geom::Point
693 SPDesktop::point() const
695     Geom::Point p = _widget->getPointer();
696     Geom::Point pw = sp_canvas_window_to_world (canvas, p);
697     p = w2d(pw);
699     Geom::Rect const r = canvas->getViewbox();
701     Geom::Point r0 = w2d(r.min());
702     Geom::Point r1 = w2d(r.max());
704     if (p[Geom::X] >= r0[Geom::X] &&
705         p[Geom::X] <= r1[Geom::X] &&
706         p[Geom::Y] >= r1[Geom::Y] &&
707         p[Geom::Y] <= r0[Geom::Y])
708     {
709         return p;
710     } else {
711         return (r0 + r1) / 2;
712     }
715 /**
716  * Put current zoom data in history list.
717  */
718 void
719 SPDesktop::push_current_zoom (GList **history)
721     Geom::Rect const area = get_display_area();
723     NRRect *old_zoom = g_new(NRRect, 1);
724     old_zoom->x0 = area.min()[Geom::X];
725     old_zoom->x1 = area.max()[Geom::X];
726     old_zoom->y0 = area.min()[Geom::Y];
727     old_zoom->y1 = area.max()[Geom::Y];
728     if ( *history == NULL
729          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
730                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
731                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
732                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
733     {
734         *history = g_list_prepend (*history, old_zoom);
735     }
738 /**
739  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
740  */
741 void
742 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
744     g_assert(_widget);
746     // save the zoom
747     if (log) {
748         push_current_zoom(&zooms_past);
749         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
750         g_list_free (zooms_future);
751         zooms_future = NULL;
752     }
754     double const cx = 0.5 * (x0 + x1);
755     double const cy = 0.5 * (y0 + y1);
757     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
758     Geom::Rect viewbox = canvas->getViewbox();
759     viewbox.expandBy(border);
761     double scale = _d2w.descrim();
762     double newscale;
763     if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
764         newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
765     } else {
766         newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
767     }
769     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
771     int clear = FALSE;
772     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
773         /* Set zoom factors */
774         _d2w = Geom::Scale(newscale, -newscale);
775         _w2d = Geom::Scale(1/newscale, 1/-newscale);
776         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
777         clear = TRUE;
778     }
780     /* Calculate top left corner (in document pixels) */
781     x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
782     y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
784     /* Scroll */
785     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
787     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
788     sp_box3d_context_update_lines(event_context);
790     _widget->updateRulers();
791     _widget->updateScrollbars(_d2w.descrim());
792     _widget->updateZoom();
795 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
797     set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
800 /**
801  * Return viewbox dimensions.
802  */
803 Geom::Rect SPDesktop::get_display_area() const
805     Geom::Rect const viewbox = canvas->getViewbox();
807     double const scale = _d2w[0];
809     return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
810                       Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
813 /**
814  * Revert back to previous zoom if possible.
815  */
816 void
817 SPDesktop::prev_zoom()
819     if (zooms_past == NULL) {
820         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
821         return;
822     }
824     // push current zoom into forward zooms list
825     push_current_zoom (&zooms_future);
827     // restore previous zoom
828     set_display_area (((NRRect *) zooms_past->data)->x0,
829             ((NRRect *) zooms_past->data)->y0,
830             ((NRRect *) zooms_past->data)->x1,
831             ((NRRect *) zooms_past->data)->y1,
832             0, false);
834     // remove the just-added zoom from the past zooms list
835     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
838 /**
839  * Set zoom to next in list.
840  */
841 void
842 SPDesktop::next_zoom()
844     if (zooms_future == NULL) {
845         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
846         return;
847     }
849     // push current zoom into past zooms list
850     push_current_zoom (&zooms_past);
852     // restore next zoom
853     set_display_area (((NRRect *) zooms_future->data)->x0,
854             ((NRRect *) zooms_future->data)->y0,
855             ((NRRect *) zooms_future->data)->x1,
856             ((NRRect *) zooms_future->data)->y1,
857             0, false);
859     // remove the just-used zoom from the zooms_future list
860     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
863 #include "tools-switch.h"
864 #include "node-context.h"
865 #include "shape-editor.h"
866 #include "nodepath.h"
868 /** \brief  Performs a quick zoom into what the user is working on
869     \param  enable  Whether we're going in or out of quick zoom
871 */
872 void 
873 SPDesktop::zoom_quick (bool enable)
875     if (enable == _quick_zoom_enabled) {
876         return;
877     }
879     if (enable == true) {
880         _quick_zoom_stored_area = get_display_area();
881         bool zoomed = false;
883         if (!zoomed) {
884             SPItem * singleItem = selection->singleItem();
885             if (singleItem != NULL && tools_isactive(this, TOOLS_NODES)) {
886                 SPNodeContext * ncontext = SP_NODE_CONTEXT(event_context);
888                 Inkscape::NodePath::Path * nodepath = ncontext->shape_editor->get_nodepath();
889                 // printf("I've got a nodepath, crazy\n");
891                 Geom::Rect nodes;
892                 bool firstnode = true;
894                 if (nodepath->selected) {
895                     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
896                        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
897                         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
898                            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
899                             if (node->selected) {
900                                 // printf("\tSelected node\n");
901                                 if (firstnode) {
902                                     nodes = Geom::Rect(node->pos, node->pos);
903                                     firstnode = false;
904                                 } else {
905                                     nodes.expandTo(node->pos);
906                                 }
908                                 if (node->p.other != NULL) {
909                                     /* Include previous node pos */
910                                     nodes.expandTo(node->p.other->pos);
912                                     /* Include previous handle */
913                                     if (!sp_node_side_is_line(node, &node->p)) {
914                                         nodes.expandTo(node->p.pos);
915                                     }
916                                 }
918                                 if (node->n.other != NULL) {
919                                     /* Include previous node pos */
920                                     nodes.expandTo(node->n.other->pos);
922                                     /* Include previous handle */
923                                     if (!sp_node_side_is_line(node, &node->n)) {
924                                         nodes.expandTo(node->n.pos);
925                                     }
926                                 }
927                             }
928                         }
929                     }
931                     if (!firstnode && nodes.area() * 2.0 < _quick_zoom_stored_area.area()) {
932                         set_display_area(nodes, 10);
933                         zoomed = true;
934                     }
935                 }
936             }
937         }
939         if (!zoomed) {
940             boost::optional<Geom::Rect> const d = selection->bounds();
941             if (d && !d->isEmpty() && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
942                 set_display_area(*d, 10);
943                 zoomed = true;
944             } 
945         }
947         if (!zoomed) {
948             zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
949             zoomed = true;
950         }
951     } else {
952         set_display_area(_quick_zoom_stored_area, 0);
953     }
955     _quick_zoom_enabled = enable;
956     return;
959 /**
960  * Zoom to point with absolute zoom factor.
961  */
962 void
963 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
965     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
967     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
968     // this check prevents "sliding" when trying to zoom in at maximum zoom;
969     /// \todo someone please fix calculations properly and remove this hack
970     if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
971         return;
973     Geom::Rect const viewbox = canvas->getViewbox();
975     double const width2 = viewbox.dimensions()[Geom::X] / zoom;
976     double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
978     set_display_area(cx - px * width2,
979                      cy - py * height2,
980                      cx + (1 - px) * width2,
981                      cy + (1 - py) * height2,
982                      0.0);
985 /**
986  * Zoom to center with absolute zoom factor.
987  */
988 void
989 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
991     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
994 /**
995  * Zoom to point with relative zoom factor.
996  */
997 void
998 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
1000     Geom::Rect const area = get_display_area();
1002     if (cx < area.min()[Geom::X]) {
1003         cx = area.min()[Geom::X];
1004     }
1005     if (cx > area.max()[Geom::X]) {
1006         cx = area.max()[Geom::X];
1007     }
1008     if (cy < area.min()[Geom::Y]) {
1009         cy = area.min()[Geom::Y];
1010     }
1011     if (cy > area.max()[Geom::Y]) {
1012         cy = area.max()[Geom::Y];
1013     }
1015     gdouble const scale = _d2w.descrim() * zoom;
1016     double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
1017     double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
1019     zoom_absolute_keep_point(cx, cy, px, py, scale);
1022 /**
1023  * Zoom to center with relative zoom factor.
1024  */
1025 void
1026 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1028     gdouble scale = _d2w.descrim() * zoom;
1029     zoom_absolute (cx, cy, scale);
1032 /**
1033  * Set display area to origin and current document dimensions.
1034  */
1035 void
1036 SPDesktop::zoom_page()
1038     Geom::Rect d(Geom::Point(0, 0),
1039                  Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1041     // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 1.0; is it safe to ignore it?
1042     if (d.isEmpty()) {
1043         return;
1044     }
1046     set_display_area(d, 10);
1049 /**
1050  * Set display area to current document width.
1051  */
1052 void
1053 SPDesktop::zoom_page_width()
1055     Geom::Rect const a = get_display_area();
1057     if (sp_document_width(doc()) < 1.0) {
1058         return;
1059     }
1061     Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1062                  Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1064     set_display_area(d, 10);
1067 /**
1068  * Zoom to selection.
1069  */
1070 void
1071 SPDesktop::zoom_selection()
1073     boost::optional<Geom::Rect> const d = selection->bounds();
1075     // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 0.1; is it safe to ignore it?
1076     if ( !d || d->isEmpty() ) {
1077         return;
1078     }
1080     set_display_area(*d, 10);
1083 /**
1084  * Tell widget to let zoom widget grab keyboard focus.
1085  */
1086 void
1087 SPDesktop::zoom_grab_focus()
1089     _widget->letZoomGrabFocus();
1092 /**
1093  * Zoom to whole drawing.
1094  */
1095 void
1096 SPDesktop::zoom_drawing()
1098     g_return_if_fail (doc() != NULL);
1099     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1100     g_return_if_fail (docitem != NULL);
1102     boost::optional<Geom::Rect> d = sp_item_bbox_desktop(docitem);
1104     /* Note that the second condition here indicates that
1105     ** there are no items in the drawing.
1106     */
1107     // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 1.0; is it safe to ignore it?
1108     if ( !d || d->isEmpty() ) {
1109         return;
1110     }
1112     set_display_area(*d, 10);
1115 /**
1116  * Scroll canvas by specific coordinate amount in svg coordinates.
1117  */
1118 void
1119 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1121     double scale = _d2w.descrim();
1122     scroll_world(dx*scale, dy*scale, is_scrolling);
1125 /**
1126  * Scroll canvas by specific coordinate amount.
1127  */
1128 void
1129 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1131     g_assert(_widget);
1133     Geom::Rect const viewbox = canvas->getViewbox();
1135     sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1137     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1138     sp_box3d_context_update_lines(event_context);
1140     _widget->updateRulers();
1141     _widget->updateScrollbars(_d2w.descrim());
1144 bool
1145 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1147     using Geom::X;
1148     using Geom::Y;
1150     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1151     gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1153     // autoscrolldistance is in screen pixels, but the display area is in document units
1154     autoscrolldistance /= _d2w.descrim();
1155     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1156     Geom::Rect dbox = get_display_area();
1157     dbox.expandBy(-autoscrolldistance);
1159     if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1160         !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y])   ) {
1162         Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1164         gdouble x_to;
1165         if (p[X] < dbox.min()[X])
1166             x_to = dbox.min()[X];
1167         else if (p[X] > dbox.max()[X])
1168             x_to = dbox.max()[X];
1169         else
1170             x_to = p[X];
1172         gdouble y_to;
1173         if (p[Y] < dbox.min()[Y])
1174             y_to = dbox.min()[Y];
1175         else if (p[Y] > dbox.max()[Y])
1176             y_to = dbox.max()[Y];
1177         else
1178             y_to = p[Y];
1180         Geom::Point const d_dt(x_to, y_to);
1181         Geom::Point const d_w( d_dt * _d2w );
1182         Geom::Point const moved_w( d_w - s_w );
1184         if (autoscrollspeed == 0)
1185             autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1187         if (autoscrollspeed != 0)
1188             scroll_world (autoscrollspeed * moved_w);
1190         return true;
1191     }
1192     return false;
1195 bool
1196 SPDesktop::is_iconified()
1198     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1201 void
1202 SPDesktop::iconify()
1204     _widget->setIconified();
1207 bool
1208 SPDesktop::is_maximized()
1210     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1213 void
1214 SPDesktop::maximize()
1216     _widget->setMaximized();
1219 bool
1220 SPDesktop::is_fullscreen()
1222     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1225 void
1226 SPDesktop::fullscreen()
1228     _widget->setFullscreen();
1231 /** \brief  Checks to see if the user is working in focused mode
1233     Returns the value of \c _focusMode
1234 */
1235 bool
1236 SPDesktop::is_focusMode()
1238     return _focusMode;
1241 /** \brief  Changes whether the user is in focus mode or not
1242     \param  mode  Which mode the view should be in
1244 */
1245 void
1246 SPDesktop::focusMode (bool mode)
1248     if (mode == _focusMode) { return; }
1250     _focusMode = mode;
1252     layoutWidget();
1253     //sp_desktop_widget_layout(SPDesktopWidget);
1255     return;
1258 void
1259 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1261     _widget->getGeometry (x, y, w, h);
1264 void
1265 SPDesktop::setWindowPosition (Geom::Point p)
1267     _widget->setPosition (p);
1270 void
1271 SPDesktop::setWindowSize (gint w, gint h)
1273     _widget->setSize (w, h);
1276 void
1277 SPDesktop::setWindowTransient (void *p, int transient_policy)
1279     _widget->setTransient (p, transient_policy);
1282 Gtk::Window*
1283 SPDesktop::getToplevel( )
1285     return _widget->getWindow();
1288 void
1289 SPDesktop::presentWindow()
1291     _widget->present();
1294 bool
1295 SPDesktop::warnDialog (gchar *text)
1297     return _widget->warnDialog (text);
1300 void
1301 SPDesktop::toggleRulers()
1303     _widget->toggleRulers();
1306 void
1307 SPDesktop::toggleScrollbars()
1309     _widget->toggleScrollbars();
1312 void
1313 SPDesktop::layoutWidget()
1315     _widget->layout();
1318 void
1319 SPDesktop::destroyWidget()
1321     _widget->destroy();
1324 bool
1325 SPDesktop::shutdown()
1327     return _widget->shutdown();
1330 bool SPDesktop::onDeleteUI (GdkEventAny*)
1332     if(shutdown()) 
1333         return true;
1335     destroyWidget();
1336     return false;
1339 /**
1340  *  onWindowStateEvent
1341  *
1342  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1343  *  Since GTK doesn't have a way to query this state information directly, we
1344  *  record it for the desktop here, and also possibly trigger a layout.
1345  */
1346 bool
1347 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1349     // Record the desktop window's state
1350     window_state = event->new_window_state;
1352     // Layout may differ depending on full-screen mode or not
1353     GdkWindowState changed = event->changed_mask;
1354     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1355         layoutWidget();
1356     }
1358     return false;
1361 void
1362 SPDesktop::setToolboxFocusTo (gchar const *label)
1364     _widget->setToolboxFocusTo (label);
1367 void
1368 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1370     _widget->setToolboxAdjustmentValue (id, val);
1373 void
1374 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1376     _widget->setToolboxSelectOneValue (id, val);
1379 bool
1380 SPDesktop::isToolboxButtonActive (gchar const *id)
1382     return _widget->isToolboxButtonActive (id);
1385 void
1386 SPDesktop::emitToolSubselectionChanged(gpointer data)
1388     _tool_subselection_changed.emit(data);
1389     inkscape_subselection_changed (this);
1392 void
1393 SPDesktop::updateNow()
1395   sp_canvas_update_now(canvas);
1398 void
1399 SPDesktop::enableInteraction()
1401   _widget->enableInteraction();
1404 void SPDesktop::disableInteraction()
1406   _widget->disableInteraction();
1409 void SPDesktop::setWaitingCursor()
1411     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1412     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1413     gdk_cursor_unref(waiting);
1414     // GDK needs the flush for the cursor change to take effect
1415     gdk_flush();
1416     waiting_cursor = true;
1419 void SPDesktop::clearWaitingCursor()
1421   if (waiting_cursor)
1422       sp_event_context_update_cursor(sp_desktop_event_context(this));
1425 void SPDesktop::toggleColorProfAdjust()
1427     _widget->toggleColorProfAdjust();
1430 void SPDesktop::toggleGrids()
1432     if (namedview->grids) {
1433         if(gridgroup) {
1434             showGrids(!grids_visible);
1435         }
1436     } else {
1437         //there is no grid present at the moment. add a rectangular grid and make it visible
1438         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1439         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1440         showGrids(true);
1441     }
1444 void SPDesktop::showGrids(bool show, bool dirty_document)
1446     grids_visible = show;
1447     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1448     if (show) {
1449         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1450     } else {
1451         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1452     }
1455 void SPDesktop::toggleSnapping()
1457     bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1458     Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1459     sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1462 //----------------------------------------------------------------------
1463 // Callback implementations. The virtual ones are connected by the view.
1465 void
1466 SPDesktop::onPositionSet (double x, double y)
1468     _widget->viewSetPosition (Geom::Point(x,y));
1471 void
1472 SPDesktop::onResized (double /*x*/, double /*y*/)
1474    // Nothing called here
1477 /**
1478  * Redraw callback; queues Gtk redraw; connected by View.
1479  */
1480 void
1481 SPDesktop::onRedrawRequested ()
1483     if (main) {
1484         _widget->requestCanvasUpdate();
1485     }
1488 void
1489 SPDesktop::updateCanvasNow()
1491   _widget->requestCanvasUpdateAndWait();
1494 /**
1495  * Associate document with desktop.
1496  */
1497 void
1498 SPDesktop::setDocument (SPDocument *doc)
1500     if (this->doc() && doc) {
1501         namedview->hide(this);
1502         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1503     }
1505     if (_layer_hierarchy) {
1506         _layer_hierarchy->clear();
1507         delete _layer_hierarchy;
1508     }
1509     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1510     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1511     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1512     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1513     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1515     /* setup EventLog */
1516     event_log = new Inkscape::EventLog(doc);
1517     doc->addUndoObserver(*event_log);
1519     _commit_connection.disconnect();
1520     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1522     /// \todo fixme: This condition exists to make sure the code
1523     /// inside is NOT called on initialization, only on replacement. But there
1524     /// are surely more safe methods to accomplish this.
1525     // TODO since the comment had reversed logic, check the intent of this block of code:
1526     if (drawing) {
1527         NRArenaItem *ai = 0;
1529         namedview = sp_document_namedview (doc, NULL);
1530         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1531         number = namedview->getViewCount();
1533         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1534                 SP_CANVAS_ARENA (drawing)->arena,
1535                 dkey,
1536                 SP_ITEM_SHOW_DISPLAY);
1537         if (ai) {
1538             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1539         }
1540         namedview->show(this);
1541         /* Ugly hack */
1542         activate_guides (true);
1543         /* Ugly hack */
1544         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1545     }
1547     _document_replaced_signal.emit (this, doc);
1549     View::setDocument (doc);
1552 void
1553 SPDesktop::onStatusMessage
1554 (Inkscape::MessageType type, gchar const *message)
1556     if (_widget) {
1557         _widget->setMessage(type, message);
1558     }
1561 void
1562 SPDesktop::onDocumentURISet (gchar const* uri)
1564     _widget->setTitle(uri);
1567 /**
1568  * Resized callback.
1569  */
1570 void
1571 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1573     _doc2dt[5] = height;
1574     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1575     Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1576     SP_CTRLRECT(page)->setRectangle(a);
1577     SP_CTRLRECT(page_border)->setRectangle(a);
1581 void
1582 SPDesktop::_onActivate (SPDesktop* dt)
1584     if (!dt->_widget) return;
1585     dt->_widget->activateDesktop();
1588 void
1589 SPDesktop::_onDeactivate (SPDesktop* dt)
1591     if (!dt->_widget) return;
1592     dt->_widget->deactivateDesktop();
1595 void
1596 SPDesktop::_onSelectionModified
1597 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1599     if (!dt->_widget) return;
1600     dt->_widget->updateScrollbars (dt->_d2w.descrim());
1603 static void
1604 _onSelectionChanged
1605 (Inkscape::Selection *selection, SPDesktop *desktop)
1607     /** \todo
1608      * only change the layer for single selections, or what?
1609      * This seems reasonable -- for multiple selections there can be many
1610      * different layers involved.
1611      */
1612     SPItem *item=selection->singleItem();
1613     if (item) {
1614         SPObject *layer=desktop->layerForObject(item);
1615         if ( layer && layer != desktop->currentLayer() ) {
1616             desktop->setCurrentLayer(layer);
1617         }
1618     }
1621 /**
1622  * Calls event handler of current event context.
1623  * \param arena Unused
1624  * \todo fixme
1625  */
1626 static gint
1627 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1629     if (ai) {
1630         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1631         return sp_event_context_item_handler (desktop->event_context, spi, event);
1632     } else {
1633         return sp_event_context_root_handler (desktop->event_context, event);
1634     }
1637 static void
1638 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1639     g_return_if_fail(SP_IS_GROUP(layer));
1640     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1643 /// Callback
1644 static void
1645 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1646     g_return_if_fail(SP_IS_GROUP(layer));
1647     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1650 /// Callback
1651 static void
1652 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1653                                          SPDesktop *desktop)
1655     desktop->_layer_changed_signal.emit (bottom);
1658 /// Called when document is starting to be rebuilt.
1659 static void
1660 _reconstruction_start (SPDesktop * desktop)
1662     // printf("Desktop, starting reconstruction\n");
1663     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1664     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1666     /*
1667     GSList const * selection_objs = desktop->selection->list();
1668     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1670     }
1671     */
1672     desktop->selection->clear();
1674     // printf("Desktop, starting reconstruction end\n");
1677 /// Called when document rebuild is finished.
1678 static void
1679 _reconstruction_finish (SPDesktop * desktop)
1681     // printf("Desktop, finishing reconstruction\n");
1682     if (desktop->_reconstruction_old_layer_id == NULL)
1683         return;
1685     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1686     if (newLayer != NULL)
1687         desktop->setCurrentLayer(newLayer);
1689     g_free(desktop->_reconstruction_old_layer_id);
1690     desktop->_reconstruction_old_layer_id = NULL;
1691     // printf("Desktop, finishing reconstruction end\n");
1692     return;
1695 /**
1696  * Namedview_modified callback.
1697  */
1698 static void
1699 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1701     SPNamedView *nv=SP_NAMEDVIEW(obj);
1703     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1705         /* Recalculate snap distances */
1706         /* FIXME: why is the desktop getting involved in setting up something
1707         ** that is entirely to do with the namedview?
1708         */
1709         _update_snap_distances (desktop);
1711         /* Show/hide page background */
1712         if (nv->pagecolor & 0xff) {
1713             sp_canvas_item_show (desktop->table);
1714             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1715             sp_canvas_item_move_to_z (desktop->table, 0);
1716         } else {
1717             sp_canvas_item_hide (desktop->table);
1718         }
1720         /* Show/hide page border */
1721         if (nv->showborder) {
1722             // show
1723             sp_canvas_item_show (desktop->page_border);
1724             // set color and shadow
1725             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1726             if (nv->pageshadow) {
1727                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1728             }
1729             // place in the z-order stack
1730             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1731                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1732             } else {
1733                 int order = sp_canvas_item_order (desktop->page_border);
1734                 int morder = sp_canvas_item_order (desktop->drawing);
1735                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1736                     morder - order);
1737             }
1738         } else {
1739                 sp_canvas_item_hide (desktop->page_border);
1740                 if (nv->pageshadow) {
1741                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1742                 }
1743         }
1745         /* Show/hide page shadow */
1746         if (nv->showpageshadow && nv->pageshadow) {
1747             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1748         } else {
1749             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1750         }
1752         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1753         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1754             (SP_RGBA32_R_U(nv->pagecolor) +
1755              SP_RGBA32_G_U(nv->pagecolor) +
1756              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1757             // the background color is light or transparent, use black outline
1758             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1759         } else { // use white outline
1760             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1761         }
1762     }
1765 /**
1766  * Callback to reset snapper's distances.
1767  */
1768 static void
1769 _update_snap_distances (SPDesktop *desktop)
1771     SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1773     SPNamedView &nv = *desktop->namedview;
1775     //tell all grid snappers
1776     for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1777         Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1778         grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
1779                                                                       *nv.gridtoleranceunit,
1780                                                                       px));
1781     }
1783     nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
1784                                                                        *nv.guidetoleranceunit,
1785                                                                        px));
1786     nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
1787                                                                         *nv.objecttoleranceunit,
1788                                                                         px));
1792 Geom::Matrix SPDesktop::w2d() const
1794     return _w2d;
1797 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1799     return p * _w2d;
1802 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1804     return p * _d2w;
1807 Geom::Matrix SPDesktop::doc2dt() const
1809     return _doc2dt;
1812 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1814     return p * _doc2dt;
1817 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1819     return p * _doc2dt.inverse();
1823 /**
1824  * Pop event context from desktop's context stack. Never used.
1825  */
1826 // void
1827 // SPDesktop::pop_event_context (unsigned int key)
1828 // {
1829 //    SPEventContext *ec = NULL;
1830 //
1831 //    if (event_context && event_context->key == key) {
1832 //        g_return_if_fail (event_context);
1833 //        g_return_if_fail (event_context->next);
1834 //        ec = event_context;
1835 //        sp_event_context_deactivate (ec);
1836 //        event_context = ec->next;
1837 //        sp_event_context_activate (event_context);
1838 //        _event_context_changed_signal.emit (this, ec);
1839 //    }
1840 //
1841 //    SPEventContext *ref = event_context;
1842 //    while (ref && ref->next && ref->next->key != key)
1843 //        ref = ref->next;
1844 //
1845 //    if (ref && ref->next) {
1846 //        ec = ref->next;
1847 //        ref->next = ec->next;
1848 //    }
1849 //
1850 //    if (ec) {
1851 //        sp_event_context_finish (ec);
1852 //        g_object_unref (G_OBJECT (ec));
1853 //    }
1854 // }
1856 /*
1857   Local Variables:
1858   mode:c++
1859   c-file-style:"stroustrup"
1860   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1861   indent-tabs-mode:nil
1862   fill-column:99
1863   End:
1864 */
1865 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :