Code

- new: Print Colors Preview Dialog and rendermode
[inkscape.git] / src / desktop.cpp
1 #define __SP_DESKTOP_C__
3 /** \file
4  * Editable view implementation
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   MenTaLguY <mental@rydia.net>
9  *   bulia byak <buliabyak@users.sf.net>
10  *   Ralf Stephan <ralf@ark.in-berlin.de>
11  *   John Bintz <jcoswell@coswellproductions.org>
12  *   Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
13  *
14  * Copyright (C) 2007 Jon A. Cruz
15  * Copyright (C) 2006-2008 Johan Engelen
16  * Copyright (C) 2006 John Bintz
17  * Copyright (C) 2004 MenTaLguY
18  * Copyright (C) 1999-2002 Lauris Kaplinski
19  * Copyright (C) 2000-2001 Ximian, Inc.
20  *
21  * Released under GNU GPL, read the file 'COPYING' for more information
22  */
24 /** \class SPDesktop
25  * SPDesktop is a subclass of View, implementing an editable document
26  * canvas.  It is extensively used by many UI controls that need certain
27  * visual representations of their own.
28  *
29  * SPDesktop provides a certain set of SPCanvasItems, serving as GUI
30  * layers of different control objects. The one containing the whole
31  * document is the drawing layer. In addition to it, there are grid,
32  * guide, sketch and control layers. The sketch layer is used for
33  * temporary drawing objects, before the real objects in document are
34  * created. The control layer contains editing knots, rubberband and
35  * similar non-document UI objects.
36  *
37  * Each SPDesktop is associated with a SPNamedView node of the document
38  * tree.  Currently, all desktops are created from a single main named
39  * view, but in the future there may be support for different ones.
40  * SPNamedView serves as an in-document container for desktop-related
41  * data, like grid and guideline placement, snapping options and so on.
42  *
43  * Associated with each SPDesktop are the two most important editing
44  * related objects - SPSelection and SPEventContext.
45  *
46  * Sodipodi keeps track of the active desktop and invokes notification
47  * signals whenever it changes. UI elements can use these to update their
48  * display to the selection of the currently active editing window.
49  * (Lauris Kaplinski)
50  */
52 #ifdef HAVE_CONFIG_H
53 # include "config.h"
54 #endif
56 #include <glibmm/i18n.h>
57 #include <sigc++/functors/mem_fun.h>
58 #include <gtkmm.h>
60 #include <2geom/rect.h>
61 #include "macros.h"
62 #include "inkscape-private.h"
63 #include "desktop.h"
64 #include "desktop-events.h"
65 #include "desktop-handles.h"
66 #include "document.h"
67 #include "message-stack.h"
68 #include "selection.h"
69 #include "select-context.h"
70 #include "sp-namedview.h"
71 #include "color.h"
72 #include "sp-item-group.h"
73 #include "preferences.h"
74 #include "object-hierarchy.h"
75 #include "helper/units.h"
76 #include "display/canvas-arena.h"
77 #include "display/nr-arena.h"
78 #include "display/gnome-canvas-acetate.h"
79 #include "display/sodipodi-ctrlrect.h"
80 #include "display/sp-canvas-util.h"
81 #include "display/canvas-temporary-item-list.h"
82 #include "display/snap-indicator.h"
83 #include "ui/dialog/dialog-manager.h"
84 #include "xml/repr.h"
85 #include "message-context.h"
86 #include "device-manager.h"
87 #include "layer-fns.h"
88 #include "layer-manager.h"
89 #include "event-log.h"
90 #include "display/canvas-grid.h"
91 #include "widgets/desktop-widget.h"
92 #include "box3d-context.h"
94 #include "display/sp-canvas.h"
96 namespace Inkscape { namespace XML { class Node; }}
98 // Callback declarations
99 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
100 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
101 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
102 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
103 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
104 static void _reconstruction_start(SPDesktop * desktop);
105 static void _reconstruction_finish(SPDesktop * desktop);
106 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
108 /**
109  * Return new desktop object.
110  * \pre namedview != NULL.
111  * \pre canvas != NULL.
112  */
113 SPDesktop::SPDesktop() :
114     _dlg_mgr( 0 ),
115     namedview( 0 ),
116     canvas( 0 ),
117     selection( 0 ),
118     event_context( 0 ),
119     layer_manager( 0 ),
120     event_log( 0 ),
121     temporary_item_list( 0 ),
122     snapindicator( 0 ),
123     acetate( 0 ),
124     main( 0 ),
125     gridgroup( 0 ),
126     guides( 0 ),
127     drawing( 0 ),
128     sketch( 0 ),
129     controls( 0 ),
130     tempgroup ( 0 ),
131     table( 0 ),
132     page( 0 ),
133     page_border( 0 ),
134     current( 0 ),
135     _focusMode(false),
136     zooms_past( 0 ),
137     zooms_future( 0 ),
138     dkey( 0 ),
139     number( 0 ),
140     window_state(0),
141     interaction_disabled_counter( 0 ),
142     waiting_cursor( false ),
143     guides_active( false ),
144     gr_item( 0 ),
145     gr_point_type( 0 ),
146     gr_point_i( 0 ),
147     gr_fill_or_stroke( true ),
148     _layer_hierarchy( 0 ),
149     _reconstruction_old_layer_id( 0 ),
150     _display_mode(Inkscape::RENDERMODE_NORMAL),
151     _widget( 0 ),
152     _inkscape( 0 ),
153     _guides_message_context( 0 ),
154     _active( false ),
155     _w2d(),
156     _d2w(),
157     _doc2dt( Geom::Scale(1, -1) ),
158     grids_visible( false )
160     _d2w.setIdentity();
161     _w2d.setIdentity();
163     selection = Inkscape::GC::release( new Inkscape::Selection(this) );
166 void
167 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas, Inkscape::UI::View::EditWidgetInterface *widget)
169     _widget = widget;
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     // The order in which these canvas items are added determines the z-order. It's therefore
231     // important to add the tempgroup (which will contain the snapindicator) before adding the
232     // controls. Only this way one will be able to quickly (before the snap indicator has
233     // disappeared) reselect a node after snapping it. If the z-order is wrong however, this
234     // will not work (the snap indicator is on top of the node handler; is the snapindicator
235     // being selected? or does it intercept some of the events that should have gone to the
236     // node handler? see bug https://bugs.launchpad.net/inkscape/+bug/414142)
237     gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
238     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
239     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
240     tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
241     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
243     /* Push select tool to the bottom of stack */
244     /** \todo
245      * FIXME: this is the only call to this.  Everything else seems to just
246      * call "set" instead of "push".  Can we assume that there is only one
247      * context ever?
248      */
249     push_event_context (SP_TYPE_SELECT_CONTEXT, "/tools/select", SP_EVENT_CONTEXT_STATIC);
251     // display rect and zoom are now handled in sp_desktop_widget_realize()
253     Geom::Rect const d(Geom::Point(0.0, 0.0),
254                        Geom::Point(sp_document_width(document), sp_document_height(document)));
256     SP_CTRLRECT(page)->setRectangle(d);
257     SP_CTRLRECT(page_border)->setRectangle(d);
259     /* the following sets the page shadow on the canvas
260        It was originally set to 5, which is really cheesy!
261        It now is an attribute in the document's namedview. If a value of
262        0 is used, then the constructor for a shadow is not initialized.
263     */
265     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
266         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
267     }
270     /* Connect event for page resize */
271     _doc2dt[5] = sp_document_height (document);
272     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
274     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
276     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
277             SP_CANVAS_ARENA (drawing)->arena,
278             dkey,
279             SP_ITEM_SHOW_DISPLAY);
280     if (ai) {
281         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
282     }
284     namedview->show(this);
285     /* Ugly hack */
286     activate_guides (true);
287     /* Ugly hack */
288     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
290 /* Set up notification of rebuilding the document, this allows
291        for saving object related settings in the document. */
292     _reconstruction_start_connection =
293         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
294     _reconstruction_finish_connection =
295         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
296     _reconstruction_old_layer_id = NULL;
298     // ?
299     // sp_active_desktop_set (desktop);
300     _inkscape = INKSCAPE;
302     _activate_connection = _activate_signal.connect(
303         sigc::bind(
304             sigc::ptr_fun(_onActivate),
305             this
306         )
307     );
308      _deactivate_connection = _deactivate_signal.connect(
309         sigc::bind(
310             sigc::ptr_fun(_onDeactivate),
311             this
312         )
313     );
315     _sel_modified_connection = selection->connectModified(
316         sigc::bind(
317             sigc::ptr_fun(&_onSelectionModified),
318             this
319         )
320     );
321     _sel_changed_connection = selection->connectChanged(
322         sigc::bind(
323             sigc::ptr_fun(&_onSelectionChanged),
324             this
325         )
326     );
329     /* setup LayerManager */
330     //   (Setting up after the connections are all in place, as it may use some of them)
331     layer_manager = new Inkscape::LayerManager( this );
333     showGrids(namedview->grids_visible, false);
335     temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
336     snapindicator = new Inkscape::Display::SnapIndicator ( this );
340 void SPDesktop::destroy()
342     if (snapindicator) {
343         delete snapindicator;
344         snapindicator = NULL;
345     }
346     if (temporary_item_list) {
347         delete temporary_item_list;
348         temporary_item_list = NULL;
349     }
351     if (selection) {
352         delete selection;
353         selection = NULL;
354     }
356     namedview->hide(this);
358     _activate_connection.disconnect();
359     _deactivate_connection.disconnect();
360     _sel_modified_connection.disconnect();
361     _sel_changed_connection.disconnect();
362     _modified_connection.disconnect();
363     _commit_connection.disconnect();
364     _reconstruction_start_connection.disconnect();
365     _reconstruction_finish_connection.disconnect();
367     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
368     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
369     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
371     while (event_context) {
372         SPEventContext *ec = event_context;
373         event_context = ec->next;
374         sp_event_context_finish (ec);
375         g_object_unref (G_OBJECT (ec));
376     }
378     if (_layer_hierarchy) {
379         delete _layer_hierarchy;
380 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
381     }
383     if (layer_manager) {
384         delete layer_manager;
385         layer_manager = NULL;
386     }
388     if (_inkscape) {
389         _inkscape = NULL;
390     }
392     if (drawing) {
393         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
394         drawing = NULL;
395     }
397     delete _guides_message_context;
398     _guides_message_context = NULL;
400     g_list_free (zooms_past);
401     g_list_free (zooms_future);
404 SPDesktop::~SPDesktop() {}
406 //--------------------------------------------------------------------
407 /* Public methods */
410 /* These methods help for temporarily showing things on-canvas.
411  * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
412  * is when you want to prematurely remove the item from the canvas, by calling
413  * desktop->remove_temporary_canvasitem(tempitem).
414  */
415 /** Note that lifetime is measured in milliseconds
416  * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
417  * delete the object for you and the reference will become invalid without you knowing it.
418  * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
419  * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
420  * because the object might be deleted already without you knowing it.
421  * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
422  */
423 Inkscape::Display::TemporaryItem *
424 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
426     if (move_to_bottom) {
427         sp_canvas_item_move_to_z(item, 0);
428     }
430     return temporary_item_list->add_item(item, lifetime);
433 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
434 */
435 void
436 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
438     // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
439     if (tempitem && temporary_item_list) {
440         temporary_item_list->delete_item(tempitem);
441     }
444 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
445     SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
446     canvas->rendermode = mode;
447     _display_mode = mode;
448     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
449     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
452 void SPDesktop::displayModeToggle() {
453     switch (_display_mode) {
454     case Inkscape::RENDERMODE_NORMAL:
455         _setDisplayMode(Inkscape::RENDERMODE_NO_FILTERS);
456         break;
457     case Inkscape::RENDERMODE_NO_FILTERS:
458         _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
459         break;
460     case Inkscape::RENDERMODE_OUTLINE:
461         _setDisplayMode(Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW);
462         break;
463     case Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW:
464     default:
465         _setDisplayMode(Inkscape::RENDERMODE_NORMAL);
466     }
469 /**
470  * Returns current root (=bottom) layer.
471  */
472 SPObject *SPDesktop::currentRoot() const
474     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
477 /**
478  * Returns current top layer.
479  */
480 SPObject *SPDesktop::currentLayer() const
482     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
485 /**
486  * Sets the current layer of the desktop.
487  *
488  * Make \a object the top layer.
489  */
490 void SPDesktop::setCurrentLayer(SPObject *object) {
491     g_return_if_fail(SP_IS_GROUP(object));
492     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
493     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
494     _layer_hierarchy->setBottom(object);
497 void SPDesktop::toggleLayerSolo(SPObject *object) {
498     g_return_if_fail(SP_IS_GROUP(object));
499     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
501     bool othersShowing = false;
502     std::vector<SPObject*> layers;
503     for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
504         layers.push_back(obj);
505         othersShowing |= !SP_ITEM(obj)->isHidden();
506     }
507     for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
508         layers.push_back(obj);
509         othersShowing |= !SP_ITEM(obj)->isHidden();
510     }
513     if ( SP_ITEM(object)->isHidden() ) {
514         SP_ITEM(object)->setHidden(false);
515     }
517     for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
518         SP_ITEM(*it)->setHidden(othersShowing);
519     }
522 /**
523  * Return layer that contains \a object.
524  */
525 SPObject *SPDesktop::layerForObject(SPObject *object) {
526     g_return_val_if_fail(object != NULL, NULL);
528     SPObject *root=currentRoot();
529     object = SP_OBJECT_PARENT(object);
530     while ( object && object != root && !isLayer(object) ) {
531         object = SP_OBJECT_PARENT(object);
532     }
533     return object;
536 /**
537  * True if object is a layer.
538  */
539 bool SPDesktop::isLayer(SPObject *object) const {
540     return ( SP_IS_GROUP(object)
541              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
542                   == SPGroup::LAYER ) );
545 /**
546  * True if desktop viewport fully contains \a item's bbox.
547  */
548 bool SPDesktop::isWithinViewport (SPItem *item) const
550     Geom::Rect const viewport = get_display_area();
551     Geom::OptRect const bbox = sp_item_bbox_desktop(item);
552     if (bbox) {
553         return viewport.contains(*bbox);
554     } else {
555         return true;
556     }
559 ///
560 bool SPDesktop::itemIsHidden(SPItem const *item) const {
561     return item->isHidden(this->dkey);
564 /**
565  * Set activate property of desktop; emit signal if changed.
566  */
567 void
568 SPDesktop::set_active (bool new_active)
570     if (new_active != _active) {
571         _active = new_active;
572         if (new_active) {
573             _activate_signal.emit();
574         } else {
575             _deactivate_signal.emit();
576         }
577     }
580 /**
581  * Set activate status of current desktop's named view.
582  */
583 void
584 SPDesktop::activate_guides(bool activate)
586     guides_active = activate;
587     namedview->activateGuides(this, activate);
590 /**
591  * Make desktop switch documents.
592  */
593 void
594 SPDesktop::change_document (SPDocument *theDocument)
596     g_return_if_fail (theDocument != NULL);
598     /* unselect everything before switching documents */
599     selection->clear();
601     setDocument (theDocument);
603     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
604        (this can probably be done in a better way) */
605     Gtk::Window *parent = this->getToplevel();
606     g_assert(parent != NULL);
607     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
608     if (dtw) dtw->desktop = this;
609     sp_desktop_widget_update_namedview(dtw);
611     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
612     _document_replaced_signal.emit (this, theDocument);
615 /**
616  * Make desktop switch event contexts.
617  */
618 void
619 SPDesktop::set_event_context (GtkType type, const gchar *config)
621     SPEventContext *ec;
622     while (event_context) {
623         ec = event_context;
624         sp_event_context_deactivate (ec);
625         event_context = ec->next;
626         sp_event_context_finish (ec);
627         g_object_unref (G_OBJECT (ec));
628     }
630     ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC);
631     ec->next = event_context;
632     event_context = ec;
633     sp_event_context_activate (ec);
634     _event_context_changed_signal.emit (this, ec);
637 /**
638  * Push event context onto desktop's context stack.
639  */
640 void
641 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
643     SPEventContext *ref, *ec;
645     if (event_context && event_context->key == key) return;
646     ref = event_context;
647     while (ref && ref->next && ref->next->key != key) ref = ref->next;
648     if (ref && ref->next) {
649         ec = ref->next;
650         ref->next = ec->next;
651         sp_event_context_finish (ec);
652         g_object_unref (G_OBJECT (ec));
653     }
655     if (event_context) sp_event_context_deactivate (event_context);
656     ec = sp_event_context_new (type, this, config, key);
657     ec->next = event_context;
658     event_context = ec;
659     sp_event_context_activate (ec);
660     _event_context_changed_signal.emit (this, ec);
663 /**
664  * Sets the coordinate status to a given point
665  */
666 void
667 SPDesktop::set_coordinate_status (Geom::Point p) {
668     _widget->setCoordinateStatus(p);
671 /**
672  * \see sp_document_item_from_list_at_point_bottom()
673  */
674 SPItem *
675 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
677     g_return_val_if_fail (doc() != NULL, NULL);
678     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
681 /**
682  * \see sp_document_item_at_point()
683  */
684 SPItem *
685 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
687     g_return_val_if_fail (doc() != NULL, NULL);
688     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
691 /**
692  * \see sp_document_group_at_point()
693  */
694 SPItem *
695 SPDesktop::group_at_point (Geom::Point const p) const
697     g_return_val_if_fail (doc() != NULL, NULL);
698     return sp_document_group_at_point (doc(), dkey, p);
701 /**
702  * \brief  Returns the mouse point in document coordinates; if mouse is
703  * outside the canvas, returns the center of canvas viewpoint
704  */
705 Geom::Point
706 SPDesktop::point() const
708     Geom::Point p = _widget->getPointer();
709     Geom::Point pw = sp_canvas_window_to_world (canvas, p);
710     p = w2d(pw);
712     Geom::Rect const r = canvas->getViewbox();
714     Geom::Point r0 = w2d(r.min());
715     Geom::Point r1 = w2d(r.max());
717     if (p[Geom::X] >= r0[Geom::X] &&
718         p[Geom::X] <= r1[Geom::X] &&
719         p[Geom::Y] >= r1[Geom::Y] &&
720         p[Geom::Y] <= r0[Geom::Y])
721     {
722         return p;
723     } else {
724         return (r0 + r1) / 2;
725     }
728 /**
729  * Put current zoom data in history list.
730  */
731 void
732 SPDesktop::push_current_zoom (GList **history)
734     Geom::Rect const area = get_display_area();
736     NRRect *old_zoom = g_new(NRRect, 1);
737     old_zoom->x0 = area.min()[Geom::X];
738     old_zoom->x1 = area.max()[Geom::X];
739     old_zoom->y0 = area.min()[Geom::Y];
740     old_zoom->y1 = area.max()[Geom::Y];
741     if ( *history == NULL
742          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
743                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
744                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
745                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
746     {
747         *history = g_list_prepend (*history, old_zoom);
748     }
751 /**
752  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
753  */
754 void
755 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
757     g_assert(_widget);
759     // save the zoom
760     if (log) {
761         push_current_zoom(&zooms_past);
762         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
763         g_list_free (zooms_future);
764         zooms_future = NULL;
765     }
767     double const cx = 0.5 * (x0 + x1);
768     double const cy = 0.5 * (y0 + y1);
770     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
771     Geom::Rect viewbox = canvas->getViewbox();
772     viewbox.expandBy(-border);
774     double scale = _d2w.descrim();
775     double newscale;
776     if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
777         newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
778     } else {
779         newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
780     }
782     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
784     int clear = FALSE;
785     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
786         /* Set zoom factors */
787         _d2w = Geom::Scale(newscale, -newscale);
788         _w2d = Geom::Scale(1/newscale, 1/-newscale);
789         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
790         clear = TRUE;
791     }
793     /* Calculate top left corner (in document pixels) */
794     x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale;
795     y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale;
797     /* Scroll */
798     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
800     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
801     sp_box3d_context_update_lines(event_context);
803     _widget->updateRulers();
804     _widget->updateScrollbars(_d2w.descrim());
805     _widget->updateZoom();
808 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
810     set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
813 /**
814  * Return viewbox dimensions.
815  */
816 Geom::Rect SPDesktop::get_display_area() const
818     Geom::Rect const viewbox = canvas->getViewbox();
820     double const scale = _d2w[0];
822     return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
823                       Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
826 /**
827  * Revert back to previous zoom if possible.
828  */
829 void
830 SPDesktop::prev_zoom()
832     if (zooms_past == NULL) {
833         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
834         return;
835     }
837     // push current zoom into forward zooms list
838     push_current_zoom (&zooms_future);
840     // restore previous zoom
841     set_display_area (((NRRect *) zooms_past->data)->x0,
842             ((NRRect *) zooms_past->data)->y0,
843             ((NRRect *) zooms_past->data)->x1,
844             ((NRRect *) zooms_past->data)->y1,
845             0, false);
847     // remove the just-added zoom from the past zooms list
848     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
851 /**
852  * Set zoom to next in list.
853  */
854 void
855 SPDesktop::next_zoom()
857     if (zooms_future == NULL) {
858         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
859         return;
860     }
862     // push current zoom into past zooms list
863     push_current_zoom (&zooms_past);
865     // restore next zoom
866     set_display_area (((NRRect *) zooms_future->data)->x0,
867             ((NRRect *) zooms_future->data)->y0,
868             ((NRRect *) zooms_future->data)->x1,
869             ((NRRect *) zooms_future->data)->y1,
870             0, false);
872     // remove the just-used zoom from the zooms_future list
873     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
876 #include "tools-switch.h"
877 #include "node-context.h"
878 #include "shape-editor.h"
879 #include "nodepath.h"
881 /** \brief  Performs a quick zoom into what the user is working on
882     \param  enable  Whether we're going in or out of quick zoom
884 */
885 void
886 SPDesktop::zoom_quick (bool enable)
888     if (enable == _quick_zoom_enabled) {
889         return;
890     }
892     if (enable == true) {
893         _quick_zoom_stored_area = get_display_area();
894         bool zoomed = false;
896         if (!zoomed) {
897             SPItem * singleItem = selection->singleItem();
898             if (singleItem != NULL && tools_isactive(this, TOOLS_NODES)) {
900                 Inkscape::NodePath::Path * nodepath = event_context->shape_editor->get_nodepath();
901                 // printf("I've got a nodepath, crazy\n");
903                                 if (nodepath) {
904                                         Geom::Rect nodes;
905                                         bool firstnode = true;
907                                         if (nodepath->selected) {
908                                                 for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
909                                                    Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
910                                                         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
911                                                            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
912                                                                 if (node->selected) {
913                                                                         // printf("\tSelected node\n");
914                                                                         if (firstnode) {
915                                                                                 nodes = Geom::Rect(node->pos, node->pos);
916                                                                                 firstnode = false;
917                                                                         } else {
918                                                                                 nodes.expandTo(node->pos);
919                                                                         }
921                                                                         if (node->p.other != NULL) {
922                                                                                 /* Include previous node pos */
923                                                                                 nodes.expandTo(node->p.other->pos);
925                                                                                 /* Include previous handle */
926                                                                                 if (!sp_node_side_is_line(node, &node->p)) {
927                                                                                         nodes.expandTo(node->p.pos);
928                                                                                 }
929                                                                         }
931                                                                         if (node->n.other != NULL) {
932                                                                                 /* Include previous node pos */
933                                                                                 nodes.expandTo(node->n.other->pos);
935                                                                                 /* Include previous handle */
936                                                                                 if (!sp_node_side_is_line(node, &node->n)) {
937                                                                                         nodes.expandTo(node->n.pos);
938                                                                                 }
939                                                                         }
940                                                                 }
941                                                         }
942                                                 }
944                                                 if (!firstnode && nodes.area() * 2.0 < _quick_zoom_stored_area.area()) {
945                                                         set_display_area(nodes, 10);
946                                                         zoomed = true;
947                                                 }
948                                         }
949                                 }
950             }
951         }
953         if (!zoomed) {
954             Geom::OptRect const d = selection->bounds();
955             if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) {
956                 set_display_area(*d, 10);
957                 zoomed = true;
958             }
959         }
961         if (!zoomed) {
962             zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0);
963             zoomed = true;
964         }
965     } else {
966         set_display_area(_quick_zoom_stored_area, 0);
967     }
969     _quick_zoom_enabled = enable;
970     return;
973 /**
974  * Zoom to point with absolute zoom factor.
975  */
976 void
977 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
979     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
981     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
982     // this check prevents "sliding" when trying to zoom in at maximum zoom;
983     /// \todo someone please fix calculations properly and remove this hack
984     if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
985         return;
987     Geom::Rect const viewbox = canvas->getViewbox();
989     double const width2 = viewbox.dimensions()[Geom::X] / zoom;
990     double const height2 = viewbox.dimensions()[Geom::Y] / zoom;
992     set_display_area(cx - px * width2,
993                      cy - py * height2,
994                      cx + (1 - px) * width2,
995                      cy + (1 - py) * height2,
996                      0.0);
999 /**
1000  * Zoom to center with absolute zoom factor.
1001  */
1002 void
1003 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
1005     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
1008 /**
1009  * Zoom to point with relative zoom factor.
1010  */
1011 void
1012 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
1014     Geom::Rect const area = get_display_area();
1016     if (cx < area.min()[Geom::X]) {
1017         cx = area.min()[Geom::X];
1018     }
1019     if (cx > area.max()[Geom::X]) {
1020         cx = area.max()[Geom::X];
1021     }
1022     if (cy < area.min()[Geom::Y]) {
1023         cy = area.min()[Geom::Y];
1024     }
1025     if (cy > area.max()[Geom::Y]) {
1026         cy = area.max()[Geom::Y];
1027     }
1029     gdouble const scale = _d2w.descrim() * zoom;
1030     double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X];
1031     double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y];
1033     zoom_absolute_keep_point(cx, cy, px, py, scale);
1036 /**
1037  * Zoom to center with relative zoom factor.
1038  */
1039 void
1040 SPDesktop::zoom_relative (double cx, double cy, double zoom)
1042     gdouble scale = _d2w.descrim() * zoom;
1043     zoom_absolute (cx, cy, scale);
1046 /**
1047  * Set display area to origin and current document dimensions.
1048  */
1049 void
1050 SPDesktop::zoom_page()
1052     Geom::Rect d(Geom::Point(0, 0),
1053                  Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
1055     if (d.minExtent() < 1.0) {
1056         return;
1057     }
1059     set_display_area(d, 10);
1062 /**
1063  * Set display area to current document width.
1064  */
1065 void
1066 SPDesktop::zoom_page_width()
1068     Geom::Rect const a = get_display_area();
1070     if (sp_document_width(doc()) < 1.0) {
1071         return;
1072     }
1074     Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
1075                  Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
1077     set_display_area(d, 10);
1080 /**
1081  * Zoom to selection.
1082  */
1083 void
1084 SPDesktop::zoom_selection()
1086     Geom::OptRect const d = selection->bounds();
1088     if ( !d || d->minExtent() < 0.1 ) {
1089         return;
1090     }
1092     set_display_area(*d, 10);
1095 /**
1096  * Tell widget to let zoom widget grab keyboard focus.
1097  */
1098 void
1099 SPDesktop::zoom_grab_focus()
1101     _widget->letZoomGrabFocus();
1104 /**
1105  * Zoom to whole drawing.
1106  */
1107 void
1108 SPDesktop::zoom_drawing()
1110     g_return_if_fail (doc() != NULL);
1111     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1112     g_return_if_fail (docitem != NULL);
1114     Geom::OptRect d = sp_item_bbox_desktop(docitem);
1116     /* Note that the second condition here indicates that
1117     ** there are no items in the drawing.
1118     */
1119     if ( !d || d->minExtent() < 0.1 ) {
1120         return;
1121     }
1123     set_display_area(*d, 10);
1126 /**
1127  * Scroll canvas by specific coordinate amount in svg coordinates.
1128  */
1129 void
1130 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1132     double scale = _d2w.descrim();
1133     scroll_world(dx*scale, dy*scale, is_scrolling);
1136 /**
1137  * Scroll canvas by specific coordinate amount.
1138  */
1139 void
1140 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1142     g_assert(_widget);
1144     Geom::Rect const viewbox = canvas->getViewbox();
1146     sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1148     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1149     sp_box3d_context_update_lines(event_context);
1151     _widget->updateRulers();
1152     _widget->updateScrollbars(_d2w.descrim());
1155 bool
1156 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1158     using Geom::X;
1159     using Geom::Y;
1161     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1162     gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000);
1164     // autoscrolldistance is in screen pixels, but the display area is in document units
1165     autoscrolldistance /= _d2w.descrim();
1166     // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1167     Geom::Rect dbox = get_display_area();
1168     dbox.expandBy(-autoscrolldistance);
1170     if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) ||
1171         !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y])   ) {
1173         Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1175         gdouble x_to;
1176         if (p[X] < dbox.min()[X])
1177             x_to = dbox.min()[X];
1178         else if (p[X] > dbox.max()[X])
1179             x_to = dbox.max()[X];
1180         else
1181             x_to = p[X];
1183         gdouble y_to;
1184         if (p[Y] < dbox.min()[Y])
1185             y_to = dbox.min()[Y];
1186         else if (p[Y] > dbox.max()[Y])
1187             y_to = dbox.max()[Y];
1188         else
1189             y_to = p[Y];
1191         Geom::Point const d_dt(x_to, y_to);
1192         Geom::Point const d_w( d_dt * _d2w );
1193         Geom::Point const moved_w( d_w - s_w );
1195         if (autoscrollspeed == 0)
1196             autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10);
1198         if (autoscrollspeed != 0)
1199             scroll_world (autoscrollspeed * moved_w);
1201         return true;
1202     }
1203     return false;
1206 bool
1207 SPDesktop::is_iconified()
1209     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1212 void
1213 SPDesktop::iconify()
1215     _widget->setIconified();
1218 bool
1219 SPDesktop::is_maximized()
1221     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1224 void
1225 SPDesktop::maximize()
1227     _widget->setMaximized();
1230 bool
1231 SPDesktop::is_fullscreen()
1233     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1236 void
1237 SPDesktop::fullscreen()
1239     _widget->setFullscreen();
1242 /** \brief  Checks to see if the user is working in focused mode
1244     Returns the value of \c _focusMode
1245 */
1246 bool
1247 SPDesktop::is_focusMode()
1249     return _focusMode;
1252 /** \brief  Changes whether the user is in focus mode or not
1253     \param  mode  Which mode the view should be in
1255 */
1256 void
1257 SPDesktop::focusMode (bool mode)
1259     if (mode == _focusMode) { return; }
1261     _focusMode = mode;
1263     layoutWidget();
1264     //sp_desktop_widget_layout(SPDesktopWidget);
1266     return;
1269 void
1270 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1272     _widget->getGeometry (x, y, w, h);
1275 void
1276 SPDesktop::setWindowPosition (Geom::Point p)
1278     _widget->setPosition (p);
1281 void
1282 SPDesktop::setWindowSize (gint w, gint h)
1284     _widget->setSize (w, h);
1287 void
1288 SPDesktop::setWindowTransient (void *p, int transient_policy)
1290     _widget->setTransient (p, transient_policy);
1293 Gtk::Window*
1294 SPDesktop::getToplevel( )
1296     return _widget->getWindow();
1299 void
1300 SPDesktop::presentWindow()
1302     _widget->present();
1305 bool
1306 SPDesktop::warnDialog (gchar *text)
1308     return _widget->warnDialog (text);
1311 void
1312 SPDesktop::toggleRulers()
1314     _widget->toggleRulers();
1317 void
1318 SPDesktop::toggleScrollbars()
1320     _widget->toggleScrollbars();
1323 void
1324 SPDesktop::layoutWidget()
1326     _widget->layout();
1329 void
1330 SPDesktop::destroyWidget()
1332     _widget->destroy();
1335 bool
1336 SPDesktop::shutdown()
1338     return _widget->shutdown();
1341 bool SPDesktop::onDeleteUI (GdkEventAny*)
1343     if(shutdown())
1344         return true;
1346     destroyWidget();
1347     return false;
1350 /**
1351  *  onWindowStateEvent
1352  *
1353  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1354  *  Since GTK doesn't have a way to query this state information directly, we
1355  *  record it for the desktop here, and also possibly trigger a layout.
1356  */
1357 bool
1358 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1360     // Record the desktop window's state
1361     window_state = event->new_window_state;
1363     // Layout may differ depending on full-screen mode or not
1364     GdkWindowState changed = event->changed_mask;
1365     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1366         layoutWidget();
1367     }
1369     return false;
1372 void
1373 SPDesktop::setToolboxFocusTo (gchar const *label)
1375     _widget->setToolboxFocusTo (label);
1378 void
1379 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1381     _widget->setToolboxAdjustmentValue (id, val);
1384 void
1385 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1387     _widget->setToolboxSelectOneValue (id, val);
1390 bool
1391 SPDesktop::isToolboxButtonActive (gchar const *id)
1393     return _widget->isToolboxButtonActive (id);
1396 void
1397 SPDesktop::emitToolSubselectionChanged(gpointer data)
1399     _tool_subselection_changed.emit(data);
1400     inkscape_subselection_changed (this);
1403 void
1404 SPDesktop::updateNow()
1406   sp_canvas_update_now(canvas);
1409 void
1410 SPDesktop::enableInteraction()
1412   _widget->enableInteraction();
1415 void SPDesktop::disableInteraction()
1417   _widget->disableInteraction();
1420 void SPDesktop::setWaitingCursor()
1422     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1423     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1424     gdk_cursor_unref(waiting);
1425     // GDK needs the flush for the cursor change to take effect
1426     gdk_flush();
1427     waiting_cursor = true;
1430 void SPDesktop::clearWaitingCursor()
1432   if (waiting_cursor)
1433       sp_event_context_update_cursor(sp_desktop_event_context(this));
1436 void SPDesktop::toggleColorProfAdjust()
1438     _widget->toggleColorProfAdjust();
1441 void SPDesktop::toggleGrids()
1443     if (namedview->grids) {
1444         if(gridgroup) {
1445             showGrids(!grids_visible);
1446         }
1447     } else {
1448         //there is no grid present at the moment. add a rectangular grid and make it visible
1449         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1450         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1451         showGrids(true);
1452     }
1455 void SPDesktop::showGrids(bool show, bool dirty_document)
1457     grids_visible = show;
1458     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1459     if (show) {
1460         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1461     } else {
1462         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1463     }
1466 void SPDesktop::toggleSnapGlobal()
1468     bool v = namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
1469     Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1470     sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1473 //----------------------------------------------------------------------
1474 // Callback implementations. The virtual ones are connected by the view.
1476 void
1477 SPDesktop::onPositionSet (double x, double y)
1479     _widget->viewSetPosition (Geom::Point(x,y));
1482 void
1483 SPDesktop::onResized (double /*x*/, double /*y*/)
1485    // Nothing called here
1488 /**
1489  * Redraw callback; queues Gtk redraw; connected by View.
1490  */
1491 void
1492 SPDesktop::onRedrawRequested ()
1494     if (main) {
1495         _widget->requestCanvasUpdate();
1496     }
1499 void
1500 SPDesktop::updateCanvasNow()
1502   _widget->requestCanvasUpdateAndWait();
1505 /**
1506  * Associate document with desktop.
1507  */
1508 void
1509 SPDesktop::setDocument (SPDocument *doc)
1511     if (this->doc() && doc) {
1512         namedview->hide(this);
1513         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1514     }
1516     if (_layer_hierarchy) {
1517         _layer_hierarchy->clear();
1518         delete _layer_hierarchy;
1519     }
1520     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1521     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1522     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1523     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1524     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1526     /* setup EventLog */
1527     event_log = new Inkscape::EventLog(doc);
1528     doc->addUndoObserver(*event_log);
1530     _commit_connection.disconnect();
1531     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1533     /// \todo fixme: This condition exists to make sure the code
1534     /// inside is NOT called on initialization, only on replacement. But there
1535     /// are surely more safe methods to accomplish this.
1536     // TODO since the comment had reversed logic, check the intent of this block of code:
1537     if (drawing) {
1538         NRArenaItem *ai = 0;
1540         namedview = sp_document_namedview (doc, NULL);
1541         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1542         number = namedview->getViewCount();
1544         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1545                 SP_CANVAS_ARENA (drawing)->arena,
1546                 dkey,
1547                 SP_ITEM_SHOW_DISPLAY);
1548         if (ai) {
1549             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1550         }
1551         namedview->show(this);
1552         /* Ugly hack */
1553         activate_guides (true);
1554         /* Ugly hack */
1555         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1556     }
1558     _document_replaced_signal.emit (this, doc);
1560     View::setDocument (doc);
1563 void
1564 SPDesktop::onStatusMessage
1565 (Inkscape::MessageType type, gchar const *message)
1567     if (_widget) {
1568         _widget->setMessage(type, message);
1569     }
1572 void
1573 SPDesktop::onDocumentURISet (gchar const* uri)
1575     _widget->setTitle(uri);
1578 /**
1579  * Resized callback.
1580  */
1581 void
1582 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1584     _doc2dt[5] = height;
1585     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1586     Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1587     SP_CTRLRECT(page)->setRectangle(a);
1588     SP_CTRLRECT(page_border)->setRectangle(a);
1592 void
1593 SPDesktop::_onActivate (SPDesktop* dt)
1595     if (!dt->_widget) return;
1596     dt->_widget->activateDesktop();
1599 void
1600 SPDesktop::_onDeactivate (SPDesktop* dt)
1602     if (!dt->_widget) return;
1603     dt->_widget->deactivateDesktop();
1606 void
1607 SPDesktop::_onSelectionModified
1608 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1610     if (!dt->_widget) return;
1611     dt->_widget->updateScrollbars (dt->_d2w.descrim());
1614 static void
1615 _onSelectionChanged
1616 (Inkscape::Selection *selection, SPDesktop *desktop)
1618     /** \todo
1619      * only change the layer for single selections, or what?
1620      * This seems reasonable -- for multiple selections there can be many
1621      * different layers involved.
1622      */
1623     SPItem *item=selection->singleItem();
1624     if (item) {
1625         SPObject *layer=desktop->layerForObject(item);
1626         if ( layer && layer != desktop->currentLayer() ) {
1627             desktop->setCurrentLayer(layer);
1628         }
1629     }
1632 /**
1633  * Calls event handler of current event context.
1634  * \param arena Unused
1635  * \todo fixme
1636  */
1637 static gint
1638 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1640     if (ai) {
1641         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1642         return sp_event_context_item_handler (desktop->event_context, spi, event);
1643     } else {
1644         return sp_event_context_root_handler (desktop->event_context, event);
1645     }
1648 static void
1649 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1650     g_return_if_fail(SP_IS_GROUP(layer));
1651     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1654 /// Callback
1655 static void
1656 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1657     g_return_if_fail(SP_IS_GROUP(layer));
1658     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1661 /// Callback
1662 static void
1663 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1664                                          SPDesktop *desktop)
1666     desktop->_layer_changed_signal.emit (bottom);
1669 /// Called when document is starting to be rebuilt.
1670 static void
1671 _reconstruction_start (SPDesktop * desktop)
1673     // printf("Desktop, starting reconstruction\n");
1674     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1675     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1677     /*
1678     GSList const * selection_objs = desktop->selection->list();
1679     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1681     }
1682     */
1683     desktop->selection->clear();
1685     // printf("Desktop, starting reconstruction end\n");
1688 /// Called when document rebuild is finished.
1689 static void
1690 _reconstruction_finish (SPDesktop * desktop)
1692     // printf("Desktop, finishing reconstruction\n");
1693     if (desktop->_reconstruction_old_layer_id == NULL)
1694         return;
1696     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1697     if (newLayer != NULL)
1698         desktop->setCurrentLayer(newLayer);
1700     g_free(desktop->_reconstruction_old_layer_id);
1701     desktop->_reconstruction_old_layer_id = NULL;
1702     // printf("Desktop, finishing reconstruction end\n");
1703     return;
1706 /**
1707  * Namedview_modified callback.
1708  */
1709 static void
1710 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1712     SPNamedView *nv=SP_NAMEDVIEW(obj);
1714     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1716         /* Show/hide page background */
1717         if (nv->pagecolor & 0xff) {
1718             sp_canvas_item_show (desktop->table);
1719             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1720             sp_canvas_item_move_to_z (desktop->table, 0);
1721         } else {
1722             sp_canvas_item_hide (desktop->table);
1723         }
1725         /* Show/hide page border */
1726         if (nv->showborder) {
1727             // show
1728             sp_canvas_item_show (desktop->page_border);
1729             // set color and shadow
1730             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1731             if (nv->pageshadow) {
1732                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1733             }
1734             // place in the z-order stack
1735             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1736                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1737             } else {
1738                 int order = sp_canvas_item_order (desktop->page_border);
1739                 int morder = sp_canvas_item_order (desktop->drawing);
1740                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1741                     morder - order);
1742             }
1743         } else {
1744                 sp_canvas_item_hide (desktop->page_border);
1745                 if (nv->pageshadow) {
1746                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1747                 }
1748         }
1750         /* Show/hide page shadow */
1751         if (nv->showpageshadow && nv->pageshadow) {
1752             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1753         } else {
1754             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1755         }
1757         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1758         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1759             (SP_RGBA32_R_U(nv->pagecolor) +
1760              SP_RGBA32_G_U(nv->pagecolor) +
1761              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1762             // the background color is light or transparent, use black outline
1763             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/onlight", 0xff);
1764         } else { // use white outline
1765             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs->getInt("/options/wireframecolors/ondark", 0xffffffff);
1766         }
1767     }
1770 Geom::Matrix SPDesktop::w2d() const
1772     return _w2d;
1775 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1777     return p * _w2d;
1780 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1782     return p * _d2w;
1785 Geom::Matrix SPDesktop::doc2dt() const
1787     return _doc2dt;
1790 Geom::Matrix SPDesktop::dt2doc() const
1792     // doc2dt is its own inverse
1793     return _doc2dt;
1796 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1798     return p * _doc2dt;
1801 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1803     return p * dt2doc();
1807 /**
1808  * Pop event context from desktop's context stack. Never used.
1809  */
1810 // void
1811 // SPDesktop::pop_event_context (unsigned int key)
1812 // {
1813 //    SPEventContext *ec = NULL;
1814 //
1815 //    if (event_context && event_context->key == key) {
1816 //        g_return_if_fail (event_context);
1817 //        g_return_if_fail (event_context->next);
1818 //        ec = event_context;
1819 //        sp_event_context_deactivate (ec);
1820 //        event_context = ec->next;
1821 //        sp_event_context_activate (event_context);
1822 //        _event_context_changed_signal.emit (this, ec);
1823 //    }
1824 //
1825 //    SPEventContext *ref = event_context;
1826 //    while (ref && ref->next && ref->next->key != key)
1827 //        ref = ref->next;
1828 //
1829 //    if (ref && ref->next) {
1830 //        ec = ref->next;
1831 //        ref->next = ec->next;
1832 //    }
1833 //
1834 //    if (ec) {
1835 //        sp_event_context_finish (ec);
1836 //        g_object_unref (G_OBJECT (ec));
1837 //    }
1838 // }
1840 /*
1841   Local Variables:
1842   mode:c++
1843   c-file-style:"stroustrup"
1844   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1845   indent-tabs-mode:nil
1846   fill-column:99
1847   End:
1848 */
1849 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :