Code

Add simple <jar> task. Separate "builddist" target
[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 "macros.h"
61 #include "inkscape-private.h"
62 #include "desktop.h"
63 #include "desktop-events.h"
64 #include "desktop-handles.h"
65 #include "document.h"
66 #include "message-stack.h"
67 #include "selection.h"
68 #include "select-context.h"
69 #include "sp-namedview.h"
70 #include "color.h"
71 #include "sp-item-group.h"
72 #include "prefs-utils.h"
73 #include "object-hierarchy.h"
74 #include "helper/units.h"
75 #include "display/canvas-arena.h"
76 #include "display/nr-arena.h"
77 #include "display/gnome-canvas-acetate.h"
78 #include "display/sodipodi-ctrlrect.h"
79 #include "display/sp-canvas-util.h"
80 #include "display/canvas-temporary-item-list.h"
81 #include "display/snap-indicator.h"
82 #include "libnr/nr-matrix-div.h"
83 #include "libnr/nr-rect-ops.h"
84 #include "ui/dialog/dialog-manager.h"
85 #include "xml/repr.h"
86 #include "message-context.h"
87 #include "device-manager.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     zooms_past( 0 ),
137     zooms_future( 0 ),
138     dkey( 0 ),
139     number( 0 ),
140     window_state(0),
141     interaction_disabled_counter( 0 ),
142     waiting_cursor( false ),
143     guides_active( false ),
144     gr_item( 0 ),
145     gr_point_type( 0 ),
146     gr_point_i( 0 ),
147     gr_fill_or_stroke( true ),
148     _layer_hierarchy( 0 ),
149     _reconstruction_old_layer_id( 0 ),
150     _display_mode(Inkscape::RENDERMODE_NORMAL),
151     _saved_display_mode(Inkscape::RENDERMODE_NORMAL),
152     _widget( 0 ),
153     _inkscape( 0 ),
154     _guides_message_context( 0 ),
155     _active( false ),
156     _w2d(),
157     _d2w(),
158     _doc2dt( NR::Matrix(NR::scale(1, -1)) ),
159     grids_visible( false )
161     _d2w.set_identity();
162     _w2d.set_identity();
164     selection = Inkscape::GC::release( new Inkscape::Selection(this) );
167 void
168 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
170     // Temporary workaround for link order issues:
171     Inkscape::DeviceManager::getManager().getDevices();
173     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
175     current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
177     namedview = nv;
178     canvas = aCanvas;
180     SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
181     /* Kill flicker */
182     sp_document_ensure_up_to_date (document);
184     /* Setup Dialog Manager */
185     _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
187     dkey = sp_item_display_key_new (1);
189     /* Connect document */
190     setDocument (document);
192     number = namedview->getViewCount();
195     /* Setup Canvas */
196     g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
198     SPCanvasGroup *root = sp_canvas_root (canvas);
200     /* Setup adminstrative layers */
201     acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
202     g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
203     main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
204     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
206     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
207     SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
208     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
209     sp_canvas_item_move_to_z (table, 0);
211     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
212     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
213     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
215     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
216     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
218     SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
220     if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
221         // Start in outline mode
222         setDisplayModeOutline();
223     } else {
224         // Start in normal mode, default
225         setDisplayModeNormal();
226     }
228     gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
229     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
230     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
231     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
232     tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
234     /* Push select tool to the bottom of stack */
235     /** \todo
236      * FIXME: this is the only call to this.  Everything else seems to just
237      * call "set" instead of "push".  Can we assume that there is only one
238      * context ever?
239      */
240     push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
242     // display rect and zoom are now handled in sp_desktop_widget_realize()
244     NR::Rect const d(NR::Point(0.0, 0.0),
245                      NR::Point(sp_document_width(document), sp_document_height(document)));
247     SP_CTRLRECT(page)->setRectangle(d);
248     SP_CTRLRECT(page_border)->setRectangle(d);
250     /* the following sets the page shadow on the canvas
251        It was originally set to 5, which is really cheesy!
252        It now is an attribute in the document's namedview. If a value of
253        0 is used, then the constructor for a shadow is not initialized.
254     */
256     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
257         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
258     }
261     /* Connect event for page resize */
262     _doc2dt[5] = sp_document_height (document);
263     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
265     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
267     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
268             SP_CANVAS_ARENA (drawing)->arena,
269             dkey,
270             SP_ITEM_SHOW_DISPLAY);
271     if (ai) {
272         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
273         nr_arena_item_unref (ai);
274     }
276     namedview->show(this);
277     /* Ugly hack */
278     activate_guides (true);
279     /* Ugly hack */
280     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
282 /* Set up notification of rebuilding the document, this allows
283        for saving object related settings in the document. */
284     _reconstruction_start_connection =
285         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
286     _reconstruction_finish_connection =
287         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
288     _reconstruction_old_layer_id = NULL;
290     // ?
291     // sp_active_desktop_set (desktop);
292     _inkscape = INKSCAPE;
294     _activate_connection = _activate_signal.connect(
295         sigc::bind(
296             sigc::ptr_fun(_onActivate),
297             this
298         )
299     );
300      _deactivate_connection = _deactivate_signal.connect(
301         sigc::bind(
302             sigc::ptr_fun(_onDeactivate),
303             this
304         )
305     );
307     _sel_modified_connection = selection->connectModified(
308         sigc::bind(
309             sigc::ptr_fun(&_onSelectionModified),
310             this
311         )
312     );
313     _sel_changed_connection = selection->connectChanged(
314         sigc::bind(
315             sigc::ptr_fun(&_onSelectionChanged),
316             this
317         )
318     );
321     /* setup LayerManager */
322     //   (Setting up after the connections are all in place, as it may use some of them)
323     layer_manager = new Inkscape::LayerManager( this );
325     showGrids(namedview->grids_visible, false);
327     temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
328     snapindicator = new Inkscape::Display::SnapIndicator ( this );
332 void SPDesktop::destroy()
334     if (snapindicator) {
335         delete snapindicator;
336         snapindicator = NULL;
337     }
338     if (temporary_item_list) {
339         delete temporary_item_list;
340         temporary_item_list = NULL;
341     }
343     namedview->hide(this);
345     _activate_connection.disconnect();
346     _deactivate_connection.disconnect();
347     _sel_modified_connection.disconnect();
348     _sel_changed_connection.disconnect();
349     _modified_connection.disconnect();
350     _commit_connection.disconnect();
351     _reconstruction_start_connection.disconnect();
352     _reconstruction_finish_connection.disconnect();
354     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
355     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
356     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
358     while (event_context) {
359         SPEventContext *ec = event_context;
360         event_context = ec->next;
361         sp_event_context_finish (ec);
362         g_object_unref (G_OBJECT (ec));
363     }
365     if (_layer_hierarchy) {
366         delete _layer_hierarchy;
367 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
368     }
370     if (layer_manager) {
371         delete layer_manager;
372         layer_manager = NULL;
373     }
375     if (_inkscape) {
376         _inkscape = NULL;
377     }
379     if (drawing) {
380         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
381         drawing = NULL;
382     }
384     delete _guides_message_context;
385     _guides_message_context = NULL;
387     g_list_free (zooms_past);
388     g_list_free (zooms_future);
391 SPDesktop::~SPDesktop() {}
393 //--------------------------------------------------------------------
394 /* Public methods */
397 /** Note that lifetime is measured in milliseconds
398 * it is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
399 * The return value should only be used as argument for SPDesktop::remove_temporary_canvasitem, because the object might be deleted already.
400 */
401 Inkscape::Display::TemporaryItem *
402 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime)
404     return temporary_item_list->add_item(item, lifetime);
407 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
408 */
409 void
410 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
412     // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
413     if (tempitem && temporary_item_list) {
414         temporary_item_list->delete_item(tempitem);
415     }
418 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
419     SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
420     canvas->rendermode = mode;
421     _display_mode = mode;
422     if (mode != Inkscape::RENDERMODE_OUTLINE) {
423         _saved_display_mode = _display_mode;
424     }
425     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
426     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
429 void SPDesktop::displayModeToggle() {
430     if (_display_mode == Inkscape::RENDERMODE_OUTLINE) {
431         _setDisplayMode(_saved_display_mode);
432     } else {
433         _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
434     }
437 /**
438  * Returns current root (=bottom) layer.
439  */
440 SPObject *SPDesktop::currentRoot() const
442     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
445 /**
446  * Returns current top layer.
447  */
448 SPObject *SPDesktop::currentLayer() const
450     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
453 /**
454  * Sets the current layer of the desktop.
455  *
456  * Make \a object the top layer.
457  */
458 void SPDesktop::setCurrentLayer(SPObject *object) {
459     g_return_if_fail(SP_IS_GROUP(object));
460     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
461     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
462     _layer_hierarchy->setBottom(object);
465 /**
466  * Return layer that contains \a object.
467  */
468 SPObject *SPDesktop::layerForObject(SPObject *object) {
469     g_return_val_if_fail(object != NULL, NULL);
471     SPObject *root=currentRoot();
472     object = SP_OBJECT_PARENT(object);
473     while ( object && object != root && !isLayer(object) ) {
474         object = SP_OBJECT_PARENT(object);
475     }
476     return object;
479 /**
480  * True if object is a layer.
481  */
482 bool SPDesktop::isLayer(SPObject *object) const {
483     return ( SP_IS_GROUP(object)
484              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
485                   == SPGroup::LAYER ) );
488 /**
489  * True if desktop viewport fully contains \a item's bbox.
490  */
491 bool SPDesktop::isWithinViewport (SPItem *item) const
493     NR::Rect const viewport = get_display_area();
494     NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
495     if (bbox) {
496         return viewport.contains(*bbox);
497     } else {
498         return true;
499     }
502 ///
503 bool SPDesktop::itemIsHidden(SPItem const *item) const {
504     return item->isHidden(this->dkey);
507 /**
508  * Set activate property of desktop; emit signal if changed.
509  */
510 void
511 SPDesktop::set_active (bool new_active)
513     if (new_active != _active) {
514         _active = new_active;
515         if (new_active) {
516             _activate_signal.emit();
517         } else {
518             _deactivate_signal.emit();
519         }
520     }
523 /**
524  * Set activate status of current desktop's named view.
525  */
526 void
527 SPDesktop::activate_guides(bool activate)
529     guides_active = activate;
530     namedview->activateGuides(this, activate);
533 /**
534  * Make desktop switch documents.
535  */
536 void
537 SPDesktop::change_document (SPDocument *theDocument)
539     g_return_if_fail (theDocument != NULL);
541     /* unselect everything before switching documents */
542     selection->clear();
544     setDocument (theDocument);
546     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
547        (this can probably be done in a better way) */
548     Gtk::Window *parent = this->getToplevel();
549     g_assert(parent != NULL);
550     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
551     if (dtw) dtw->desktop = this;
552     sp_desktop_widget_update_namedview(dtw);
554     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
555     _document_replaced_signal.emit (this, theDocument);
558 /**
559  * Make desktop switch event contexts.
560  */
561 void
562 SPDesktop::set_event_context (GtkType type, const gchar *config)
564     SPEventContext *ec;
565     while (event_context) {
566         ec = event_context;
567         sp_event_context_deactivate (ec);
568         event_context = ec->next;
569         sp_event_context_finish (ec);
570         g_object_unref (G_OBJECT (ec));
571     }
573     Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
574     ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
575     ec->next = event_context;
576     event_context = ec;
577     sp_event_context_activate (ec);
578     _event_context_changed_signal.emit (this, ec);
581 /**
582  * Push event context onto desktop's context stack.
583  */
584 void
585 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
587     SPEventContext *ref, *ec;
588     Inkscape::XML::Node *repr;
590     if (event_context && event_context->key == key) return;
591     ref = event_context;
592     while (ref && ref->next && ref->next->key != key) ref = ref->next;
593     if (ref && ref->next) {
594         ec = ref->next;
595         ref->next = ec->next;
596         sp_event_context_finish (ec);
597         g_object_unref (G_OBJECT (ec));
598     }
600     if (event_context) sp_event_context_deactivate (event_context);
601     repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
602     ec = sp_event_context_new (type, this, repr, key);
603     ec->next = event_context;
604     event_context = ec;
605     sp_event_context_activate (ec);
606     _event_context_changed_signal.emit (this, ec);
609 /**
610  * Sets the coordinate status to a given point
611  */
612 void
613 SPDesktop::set_coordinate_status (NR::Point p) {
614     _widget->setCoordinateStatus(p);
617 /**
618  * \see sp_document_item_from_list_at_point_bottom()
619  */
620 SPItem *
621 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
623     g_return_val_if_fail (doc() != NULL, NULL);
624     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
627 /**
628  * \see sp_document_item_at_point()
629  */
630 SPItem *
631 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
633     g_return_val_if_fail (doc() != NULL, NULL);
634     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
637 /**
638  * \see sp_document_group_at_point()
639  */
640 SPItem *
641 SPDesktop::group_at_point (NR::Point const p) const
643     g_return_val_if_fail (doc() != NULL, NULL);
644     return sp_document_group_at_point (doc(), dkey, p);
647 /**
648  * \brief  Returns the mouse point in document coordinates; if mouse is
649  * outside the canvas, returns the center of canvas viewpoint
650  */
651 NR::Point
652 SPDesktop::point() const
654     NR::Point p = _widget->getPointer();
655     NR::Point pw = sp_canvas_window_to_world (canvas, p);
656     p = w2d(pw);
658     NR::Rect const r = canvas->getViewbox();
660     NR::Point r0 = w2d(r.min());
661     NR::Point r1 = w2d(r.max());
663     if (p[NR::X] >= r0[NR::X] &&
664         p[NR::X] <= r1[NR::X] &&
665         p[NR::Y] >= r1[NR::Y] &&
666         p[NR::Y] <= r0[NR::Y])
667     {
668         return p;
669     } else {
670         return (r0 + r1) / 2;
671     }
674 /**
675  * Put current zoom data in history list.
676  */
677 void
678 SPDesktop::push_current_zoom (GList **history)
680     NR::Rect const area = get_display_area();
682     NRRect *old_zoom = g_new(NRRect, 1);
683     old_zoom->x0 = area.min()[NR::X];
684     old_zoom->x1 = area.max()[NR::X];
685     old_zoom->y0 = area.min()[NR::Y];
686     old_zoom->y1 = area.max()[NR::Y];
687     if ( *history == NULL
688          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
689                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
690                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
691                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
692     {
693         *history = g_list_prepend (*history, old_zoom);
694     }
697 /**
698  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
699  */
700 void
701 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
703     g_assert(_widget);
705     // save the zoom
706     if (log) {
707         push_current_zoom(&zooms_past);
708         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
709         g_list_free (zooms_future);
710         zooms_future = NULL;
711     }
713     double const cx = 0.5 * (x0 + x1);
714     double const cy = 0.5 * (y0 + y1);
716     NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
718     double scale = expansion(_d2w);
719     double newscale;
720     if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
721         newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
722     } else {
723         newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
724     }
726     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
728     int clear = FALSE;
729     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
730         /* Set zoom factors */
731         _d2w = NR::Matrix(NR::scale(newscale, -newscale));
732         _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
733         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
734         clear = TRUE;
735     }
737     /* Calculate top left corner (in document pixels) */
738     x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
739     y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
741     /* Scroll */
742     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
744     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
745     sp_box3d_context_update_lines(event_context);
747     _widget->updateRulers();
748     _widget->updateScrollbars(expansion(_d2w));
749     _widget->updateZoom();
752 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
754     set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
757 /**
758  * Return viewbox dimensions.
759  */
760 NR::Rect SPDesktop::get_display_area() const
762     NR::Rect const viewbox = canvas->getViewbox();
764     double const scale = _d2w[0];
766     return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
767                     NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
770 /**
771  * Revert back to previous zoom if possible.
772  */
773 void
774 SPDesktop::prev_zoom()
776     if (zooms_past == NULL) {
777         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
778         return;
779     }
781     // push current zoom into forward zooms list
782     push_current_zoom (&zooms_future);
784     // restore previous zoom
785     set_display_area (((NRRect *) zooms_past->data)->x0,
786             ((NRRect *) zooms_past->data)->y0,
787             ((NRRect *) zooms_past->data)->x1,
788             ((NRRect *) zooms_past->data)->y1,
789             0, false);
791     // remove the just-added zoom from the past zooms list
792     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
795 /**
796  * Set zoom to next in list.
797  */
798 void
799 SPDesktop::next_zoom()
801     if (zooms_future == NULL) {
802         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
803         return;
804     }
806     // push current zoom into past zooms list
807     push_current_zoom (&zooms_past);
809     // restore next zoom
810     set_display_area (((NRRect *) zooms_future->data)->x0,
811             ((NRRect *) zooms_future->data)->y0,
812             ((NRRect *) zooms_future->data)->x1,
813             ((NRRect *) zooms_future->data)->y1,
814             0, false);
816     // remove the just-used zoom from the zooms_future list
817     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
820 /**
821  * Zoom to point with absolute zoom factor.
822  */
823 void
824 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
826     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
828     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
829     // this check prevents "sliding" when trying to zoom in at maximum zoom;
830     /// \todo someone please fix calculations properly and remove this hack
831     if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
832         return;
834     NR::Rect const viewbox = canvas->getViewbox();
836     double const width2 = viewbox.dimensions()[NR::X] / zoom;
837     double const height2 = viewbox.dimensions()[NR::Y] / zoom;
839     set_display_area(cx - px * width2,
840                      cy - py * height2,
841                      cx + (1 - px) * width2,
842                      cy + (1 - py) * height2,
843                      0.0);
846 /**
847  * Zoom to center with absolute zoom factor.
848  */
849 void
850 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
852     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
855 /**
856  * Zoom to point with relative zoom factor.
857  */
858 void
859 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
861     NR::Rect const area = get_display_area();
863     if (cx < area.min()[NR::X]) {
864         cx = area.min()[NR::X];
865     }
866     if (cx > area.max()[NR::X]) {
867         cx = area.max()[NR::X];
868     }
869     if (cy < area.min()[NR::Y]) {
870         cy = area.min()[NR::Y];
871     }
872     if (cy > area.max()[NR::Y]) {
873         cy = area.max()[NR::Y];
874     }
876     gdouble const scale = expansion(_d2w) * zoom;
877     double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
878     double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
880     zoom_absolute_keep_point(cx, cy, px, py, scale);
883 /**
884  * Zoom to center with relative zoom factor.
885  */
886 void
887 SPDesktop::zoom_relative (double cx, double cy, double zoom)
889     gdouble scale = expansion(_d2w) * zoom;
890     zoom_absolute (cx, cy, scale);
893 /**
894  * Set display area to origin and current document dimensions.
895  */
896 void
897 SPDesktop::zoom_page()
899     NR::Rect d(NR::Point(0, 0),
900                NR::Point(sp_document_width(doc()), sp_document_height(doc())));
902     if (d.isEmpty(1.0)) {
903         return;
904     }
906     set_display_area(d, 10);
909 /**
910  * Set display area to current document width.
911  */
912 void
913 SPDesktop::zoom_page_width()
915     NR::Rect const a = get_display_area();
917     if (sp_document_width(doc()) < 1.0) {
918         return;
919     }
921     NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
922                NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
924     set_display_area(d, 10);
927 /**
928  * Zoom to selection.
929  */
930 void
931 SPDesktop::zoom_selection()
933     NR::Maybe<NR::Rect> const d = selection->bounds();
935     if ( !d || d->isEmpty(0.1) ) {
936         return;
937     }
939     set_display_area(*d, 10);
942 /**
943  * Tell widget to let zoom widget grab keyboard focus.
944  */
945 void
946 SPDesktop::zoom_grab_focus()
948     _widget->letZoomGrabFocus();
951 /**
952  * Zoom to whole drawing.
953  */
954 void
955 SPDesktop::zoom_drawing()
957     g_return_if_fail (doc() != NULL);
958     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
959     g_return_if_fail (docitem != NULL);
961     NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
963     /* Note that the second condition here indicates that
964     ** there are no items in the drawing.
965     */
966     if ( !d || d->isEmpty(1.0) ) {
967         return;
968     }
970     set_display_area(*d, 10);
973 /**
974  * Scroll canvas by specific coordinate amount.
975  */
976 void
977 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
979     g_assert(_widget);
981     NR::Rect const viewbox = canvas->getViewbox();
983     sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
985     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
986     sp_box3d_context_update_lines(event_context);
988     _widget->updateRulers();
989     _widget->updateScrollbars(expansion(_d2w));
992 bool
993 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
995     gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
997     // autoscrolldistance is in screen pixels, but the display area is in document units
998     autoscrolldistance /= expansion(_d2w);
999     NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
1001     if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
1002         !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y])   ) {
1004         NR::Point const s_w( (*p) * _d2w );
1006         gdouble x_to;
1007         if ((*p)[NR::X] < dbox.min()[NR::X])
1008             x_to = dbox.min()[NR::X];
1009         else if ((*p)[NR::X] > dbox.max()[NR::X])
1010             x_to = dbox.max()[NR::X];
1011         else
1012             x_to = (*p)[NR::X];
1014         gdouble y_to;
1015         if ((*p)[NR::Y] < dbox.min()[NR::Y])
1016             y_to = dbox.min()[NR::Y];
1017         else if ((*p)[NR::Y] > dbox.max()[NR::Y])
1018             y_to = dbox.max()[NR::Y];
1019         else
1020             y_to = (*p)[NR::Y];
1022         NR::Point const d_dt(x_to, y_to);
1023         NR::Point const d_w( d_dt * _d2w );
1024         NR::Point const moved_w( d_w - s_w );
1026         if (autoscrollspeed == 0)
1027             autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
1029         if (autoscrollspeed != 0)
1030             scroll_world (autoscrollspeed * moved_w);
1032         return true;
1033     }
1034     return false;
1037 bool
1038 SPDesktop::is_iconified()
1040     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1043 void
1044 SPDesktop::iconify()
1046     _widget->setIconified();
1049 bool
1050 SPDesktop::is_maximized()
1052     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1055 void
1056 SPDesktop::maximize()
1058     _widget->setMaximized();
1061 bool
1062 SPDesktop::is_fullscreen()
1064     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1067 void
1068 SPDesktop::fullscreen()
1070     _widget->setFullscreen();
1073 void
1074 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1076     _widget->getGeometry (x, y, w, h);
1079 void
1080 SPDesktop::setWindowPosition (NR::Point p)
1082     _widget->setPosition (p);
1085 void
1086 SPDesktop::setWindowSize (gint w, gint h)
1088     _widget->setSize (w, h);
1091 void
1092 SPDesktop::setWindowTransient (void *p, int transient_policy)
1094     _widget->setTransient (p, transient_policy);
1097 Gtk::Window*
1098 SPDesktop::getToplevel( )
1100     return _widget->getWindow();
1103 void
1104 SPDesktop::presentWindow()
1106     _widget->present();
1109 bool
1110 SPDesktop::warnDialog (gchar *text)
1112     return _widget->warnDialog (text);
1115 void
1116 SPDesktop::toggleRulers()
1118     _widget->toggleRulers();
1121 void
1122 SPDesktop::toggleScrollbars()
1124     _widget->toggleScrollbars();
1127 void
1128 SPDesktop::layoutWidget()
1130     _widget->layout();
1133 void
1134 SPDesktop::destroyWidget()
1136     _widget->destroy();
1139 bool
1140 SPDesktop::shutdown()
1142     return _widget->shutdown();
1145 bool SPDesktop::onDeleteUI (GdkEventAny*)
1147     if(shutdown()) 
1148         return true;
1150     destroyWidget();
1151     return false;
1154 /**
1155  *  onWindowStateEvent
1156  *
1157  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1158  *  Since GTK doesn't have a way to query this state information directly, we
1159  *  record it for the desktop here, and also possibly trigger a layout.
1160  */
1161 bool
1162 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1164         // Record the desktop window's state
1165     window_state = event->new_window_state;
1167     // Layout may differ depending on full-screen mode or not
1168     GdkWindowState changed = event->changed_mask;
1169     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1170         layoutWidget();
1171     }
1173         return false;
1176 void
1177 SPDesktop::setToolboxFocusTo (gchar const *label)
1179     _widget->setToolboxFocusTo (label);
1182 void
1183 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1185     _widget->setToolboxAdjustmentValue (id, val);
1188 void
1189 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1191     _widget->setToolboxSelectOneValue (id, val);
1194 bool
1195 SPDesktop::isToolboxButtonActive (gchar const *id)
1197     return _widget->isToolboxButtonActive (id);
1200 void
1201 SPDesktop::emitToolSubselectionChanged(gpointer data)
1203         _tool_subselection_changed.emit(data);
1204         inkscape_subselection_changed (this);
1207 void
1208 SPDesktop::updateNow()
1210   sp_canvas_update_now(canvas);
1213 void
1214 SPDesktop::enableInteraction()
1216   _widget->enableInteraction();
1219 void SPDesktop::disableInteraction()
1221   _widget->disableInteraction();
1224 void SPDesktop::setWaitingCursor()
1226     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1227     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1228     gdk_cursor_unref(waiting);
1229     waiting_cursor = true;
1231     // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1232     // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1233     // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1234     // after the call to setWaitingCursor as it was before
1235     while( Gtk::Main::events_pending() )
1236        Gtk::Main::iteration();
1239 void SPDesktop::clearWaitingCursor()
1241   if (waiting_cursor)
1242       sp_event_context_update_cursor(sp_desktop_event_context(this));
1245 void SPDesktop::toggleColorProfAdjust()
1247     _widget->toggleColorProfAdjust();
1250 void SPDesktop::toggleGrids()
1252     if (namedview->grids) {
1253         if(gridgroup) {
1254             showGrids(!grids_visible);
1255         }
1256     } else {
1257         //there is no grid present at the moment. add a rectangular grid and make it visible
1258         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1259         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1260         showGrids(true);
1261     }
1264 void SPDesktop::showGrids(bool show, bool dirty_document)
1266     grids_visible = show;
1267     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1268     if (show) {
1269         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1270     } else {
1271         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1272     }
1275 void SPDesktop::toggleSnapping()
1277     bool v = namedview->snap_manager.getSnapEnabledGlobally();
1278     Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1279     sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1282 //----------------------------------------------------------------------
1283 // Callback implementations. The virtual ones are connected by the view.
1285 void
1286 SPDesktop::onPositionSet (double x, double y)
1288     _widget->viewSetPosition (NR::Point(x,y));
1291 void
1292 SPDesktop::onResized (double /*x*/, double /*y*/)
1294    // Nothing called here
1297 /**
1298  * Redraw callback; queues Gtk redraw; connected by View.
1299  */
1300 void
1301 SPDesktop::onRedrawRequested ()
1303     if (main) {
1304         _widget->requestCanvasUpdate();
1305     }
1308 void
1309 SPDesktop::updateCanvasNow()
1311   _widget->requestCanvasUpdateAndWait();
1314 /**
1315  * Associate document with desktop.
1316  */
1317 void
1318 SPDesktop::setDocument (SPDocument *doc)
1320     if (this->doc() && doc) {
1321         namedview->hide(this);
1322         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1323     }
1325     if (_layer_hierarchy) {
1326         _layer_hierarchy->clear();
1327         delete _layer_hierarchy;
1328     }
1329     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1330     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1331     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1332     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1333     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1335     /* setup EventLog */
1336     event_log = new Inkscape::EventLog(doc);
1337     doc->addUndoObserver(*event_log);
1339     _commit_connection.disconnect();
1340     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1342     /// \todo fixme: This condition exists to make sure the code
1343     /// inside is NOT called on initialization, only on replacement. But there
1344     /// are surely more safe methods to accomplish this.
1345     // TODO since the comment had reversed logic, check the intent of this block of code:
1346     if (drawing) {
1347         NRArenaItem *ai = 0;
1349         namedview = sp_document_namedview (doc, NULL);
1350         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1351         number = namedview->getViewCount();
1353         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1354                 SP_CANVAS_ARENA (drawing)->arena,
1355                 dkey,
1356                 SP_ITEM_SHOW_DISPLAY);
1357         if (ai) {
1358             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1359             nr_arena_item_unref (ai);
1360         }
1361         namedview->show(this);
1362         /* Ugly hack */
1363         activate_guides (true);
1364         /* Ugly hack */
1365         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1366     }
1368     _document_replaced_signal.emit (this, doc);
1370     View::setDocument (doc);
1373 void
1374 SPDesktop::onStatusMessage
1375 (Inkscape::MessageType type, gchar const *message)
1377     if (_widget) {
1378         _widget->setMessage(type, message);
1379     }
1382 void
1383 SPDesktop::onDocumentURISet (gchar const* uri)
1385     _widget->setTitle(uri);
1388 /**
1389  * Resized callback.
1390  */
1391 void
1392 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1394     _doc2dt[5] = height;
1395     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1396     NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1397     SP_CTRLRECT(page)->setRectangle(a);
1398     SP_CTRLRECT(page_border)->setRectangle(a);
1402 void
1403 SPDesktop::_onActivate (SPDesktop* dt)
1405     if (!dt->_widget) return;
1406     dt->_widget->activateDesktop();
1409 void
1410 SPDesktop::_onDeactivate (SPDesktop* dt)
1412     if (!dt->_widget) return;
1413     dt->_widget->deactivateDesktop();
1416 void
1417 SPDesktop::_onSelectionModified
1418 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1420     if (!dt->_widget) return;
1421     dt->_widget->updateScrollbars (expansion(dt->_d2w));
1424 static void
1425 _onSelectionChanged
1426 (Inkscape::Selection *selection, SPDesktop *desktop)
1428     /** \todo
1429      * only change the layer for single selections, or what?
1430      * This seems reasonable -- for multiple selections there can be many
1431      * different layers involved.
1432      */
1433     SPItem *item=selection->singleItem();
1434     if (item) {
1435         SPObject *layer=desktop->layerForObject(item);
1436         if ( layer && layer != desktop->currentLayer() ) {
1437             desktop->setCurrentLayer(layer);
1438         }
1439     }
1442 /**
1443  * Calls event handler of current event context.
1444  * \param arena Unused
1445  * \todo fixme
1446  */
1447 static gint
1448 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1450     if (ai) {
1451         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1452         return sp_event_context_item_handler (desktop->event_context, spi, event);
1453     } else {
1454         return sp_event_context_root_handler (desktop->event_context, event);
1455     }
1458 static void
1459 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1460     g_return_if_fail(SP_IS_GROUP(layer));
1461     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1464 /// Callback
1465 static void
1466 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1467     g_return_if_fail(SP_IS_GROUP(layer));
1468     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1471 /// Callback
1472 static void
1473 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1474                                          SPDesktop *desktop)
1476     desktop->_layer_changed_signal.emit (bottom);
1479 /// Called when document is starting to be rebuilt.
1480 static void
1481 _reconstruction_start (SPDesktop * desktop)
1483     // printf("Desktop, starting reconstruction\n");
1484     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1485     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1487     /*
1488     GSList const * selection_objs = desktop->selection->list();
1489     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1491     }
1492     */
1493     desktop->selection->clear();
1495     // printf("Desktop, starting reconstruction end\n");
1498 /// Called when document rebuild is finished.
1499 static void
1500 _reconstruction_finish (SPDesktop * desktop)
1502     // printf("Desktop, finishing reconstruction\n");
1503     if (desktop->_reconstruction_old_layer_id == NULL)
1504         return;
1506     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1507     if (newLayer != NULL)
1508         desktop->setCurrentLayer(newLayer);
1510     g_free(desktop->_reconstruction_old_layer_id);
1511     desktop->_reconstruction_old_layer_id = NULL;
1512     // printf("Desktop, finishing reconstruction end\n");
1513     return;
1516 /**
1517  * Namedview_modified callback.
1518  */
1519 static void
1520 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1522     SPNamedView *nv=SP_NAMEDVIEW(obj);
1524     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1526         /* Recalculate snap distances */
1527         /* FIXME: why is the desktop getting involved in setting up something
1528         ** that is entirely to do with the namedview?
1529         */
1530         _update_snap_distances (desktop);
1532         /* Show/hide page background */
1533         if (nv->pagecolor & 0xff) {
1534             sp_canvas_item_show (desktop->table);
1535             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1536             sp_canvas_item_move_to_z (desktop->table, 0);
1537         } else {
1538             sp_canvas_item_hide (desktop->table);
1539         }
1541         /* Show/hide page border */
1542         if (nv->showborder) {
1543             // show
1544             sp_canvas_item_show (desktop->page_border);
1545             // set color and shadow
1546             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1547             if (nv->pageshadow) {
1548                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1549             }
1550             // place in the z-order stack
1551             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1552                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1553             } else {
1554                 int order = sp_canvas_item_order (desktop->page_border);
1555                 int morder = sp_canvas_item_order (desktop->drawing);
1556                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1557                                     morder - order);
1558             }
1559         } else {
1560                 sp_canvas_item_hide (desktop->page_border);
1561                 if (nv->pageshadow) {
1562                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1563                 }
1564         }
1566         /* Show/hide page shadow */
1567         if (nv->showpageshadow && nv->pageshadow) {
1568             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1569         } else {
1570             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1571         }
1573         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1574             (SP_RGBA32_R_U(nv->pagecolor) +
1575              SP_RGBA32_G_U(nv->pagecolor) +
1576              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1577             // the background color is light or transparent, use black outline
1578             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1579         } else { // use white outline
1580             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1581         }
1582     }
1585 /**
1586  * Callback to reset snapper's distances.
1587  */
1588 static void
1589 _update_snap_distances (SPDesktop *desktop)
1591     SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1593     SPNamedView &nv = *desktop->namedview;
1595     //tell all grid snappers
1596     for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1597         Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1598         grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
1599                                                                       *nv.gridtoleranceunit,
1600                                                                       px));
1601     }
1603     nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
1604                                                                        *nv.guidetoleranceunit,
1605                                                                        px));
1606     nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
1607                                                                         *nv.objecttoleranceunit,
1608                                                                         px));
1612 NR::Matrix SPDesktop::w2d() const
1614     return _w2d;
1617 NR::Point SPDesktop::w2d(NR::Point const &p) const
1619     return p * _w2d;
1622 NR::Point SPDesktop::d2w(NR::Point const &p) const
1624     return p * _d2w;
1627 NR::Matrix SPDesktop::doc2dt() const
1629     return _doc2dt;
1632 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1634     return p * _doc2dt;
1637 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1639     return p / _doc2dt;
1643 /**
1644  * Pop event context from desktop's context stack. Never used.
1645  */
1646 // void
1647 // SPDesktop::pop_event_context (unsigned int key)
1648 // {
1649 //    SPEventContext *ec = NULL;
1650 //
1651 //    if (event_context && event_context->key == key) {
1652 //        g_return_if_fail (event_context);
1653 //        g_return_if_fail (event_context->next);
1654 //        ec = event_context;
1655 //        sp_event_context_deactivate (ec);
1656 //        event_context = ec->next;
1657 //        sp_event_context_activate (event_context);
1658 //        _event_context_changed_signal.emit (this, ec);
1659 //    }
1660 //
1661 //    SPEventContext *ref = event_context;
1662 //    while (ref && ref->next && ref->next->key != key)
1663 //        ref = ref->next;
1664 //
1665 //    if (ref && ref->next) {
1666 //        ec = ref->next;
1667 //        ref->next = ec->next;
1668 //    }
1669 //
1670 //    if (ec) {
1671 //        sp_event_context_finish (ec);
1672 //        g_object_unref (G_OBJECT (ec));
1673 //    }
1674 // }
1676 /*
1677   Local Variables:
1678   mode:c++
1679   c-file-style:"stroustrup"
1680   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1681   indent-tabs-mode:nil
1682   fill-column:99
1683   End:
1684 */
1685 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :