Code

Move 3D box code out of sp-canvas.cpp
[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 "layer-manager.h"
88 #include "event-log.h"
89 #include "display/canvas-grid.h"
90 #include "widgets/desktop-widget.h"
91 #include "box3d-context.h"
93 #include "display/sp-canvas.h"
95 namespace Inkscape { namespace XML { class Node; }}
97 // Callback declarations
98 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
99 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
100 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
101 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
102 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
103 static void _reconstruction_start(SPDesktop * desktop);
104 static void _reconstruction_finish(SPDesktop * desktop);
105 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
106 static void _update_snap_distances (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     zooms_past( 0 ),
136     zooms_future( 0 ),
137     dkey( 0 ),
138     number( 0 ),
139     window_state(0),
140     interaction_disabled_counter( 0 ),
141     waiting_cursor( false ),
142     guides_active( false ),
143     gr_item( 0 ),
144     gr_point_type( 0 ),
145     gr_point_i( 0 ),
146     gr_fill_or_stroke( true ),
147     _layer_hierarchy( 0 ),
148     _reconstruction_old_layer_id( 0 ),
149     _widget( 0 ),
150     _inkscape( 0 ),
151     _guides_message_context( 0 ),
152     _active( false ),
153     _w2d(),
154     _d2w(),
155     _doc2dt( NR::Matrix(NR::scale(1, -1)) ),
156     grids_visible( false )
158     _d2w.set_identity();
159     _w2d.set_identity();
161     selection = Inkscape::GC::release( new Inkscape::Selection(this) );
164 void
165 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
167     _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
169     current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
171     namedview = nv;
172     canvas = aCanvas;
174     SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
175     /* Kill flicker */
176     sp_document_ensure_up_to_date (document);
178     /* Setup Dialog Manager */
179     _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
181     dkey = sp_item_display_key_new (1);
183     /* Connect document */
184     setDocument (document);
186     number = namedview->getViewCount();
189     /* Setup Canvas */
190     g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
192     SPCanvasGroup *root = sp_canvas_root (canvas);
194     /* Setup adminstrative layers */
195     acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
196     g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
197     main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
198     g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
200     table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
201     SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
202     SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
203     sp_canvas_item_move_to_z (table, 0);
205     page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
206     ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
207     page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
209     drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
210     g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
212     SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
214     if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
215         // Start in outline mode
216         setDisplayModeOutline();
217     } else {
218         // Start in normal mode, default
219         setDisplayModeNormal();
220     }
222     gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
223     guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
224     sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
225     controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
226     tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
228     /* Push select tool to the bottom of stack */
229     /** \todo
230      * FIXME: this is the only call to this.  Everything else seems to just
231      * call "set" instead of "push".  Can we assume that there is only one
232      * context ever?
233      */
234     push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
236     // display rect and zoom are now handled in sp_desktop_widget_realize()
238     NR::Rect const d(NR::Point(0.0, 0.0),
239                      NR::Point(sp_document_width(document), sp_document_height(document)));
241     SP_CTRLRECT(page)->setRectangle(d);
242     SP_CTRLRECT(page_border)->setRectangle(d);
244     /* the following sets the page shadow on the canvas
245        It was originally set to 5, which is really cheesy!
246        It now is an attribute in the document's namedview. If a value of
247        0 is used, then the constructor for a shadow is not initialized.
248     */
250     if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
251         SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
252     }
255     /* Connect event for page resize */
256     _doc2dt[5] = sp_document_height (document);
257     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
259     _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
261     NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
262             SP_CANVAS_ARENA (drawing)->arena,
263             dkey,
264             SP_ITEM_SHOW_DISPLAY);
265     if (ai) {
266         nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
267         nr_arena_item_unref (ai);
268     }
270     namedview->show(this);
271     /* Ugly hack */
272     activate_guides (true);
273     /* Ugly hack */
274     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
276 /* Set up notification of rebuilding the document, this allows
277        for saving object related settings in the document. */
278     _reconstruction_start_connection =
279         document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
280     _reconstruction_finish_connection =
281         document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
282     _reconstruction_old_layer_id = NULL;
284     // ?
285     // sp_active_desktop_set (desktop);
286     _inkscape = INKSCAPE;
288     _activate_connection = _activate_signal.connect(
289         sigc::bind(
290             sigc::ptr_fun(_onActivate),
291             this
292         )
293     );
294      _deactivate_connection = _deactivate_signal.connect(
295         sigc::bind(
296             sigc::ptr_fun(_onDeactivate),
297             this
298         )
299     );
301     _sel_modified_connection = selection->connectModified(
302         sigc::bind(
303             sigc::ptr_fun(&_onSelectionModified),
304             this
305         )
306     );
307     _sel_changed_connection = selection->connectChanged(
308         sigc::bind(
309             sigc::ptr_fun(&_onSelectionChanged),
310             this
311         )
312     );
315     /* setup LayerManager */
316     //   (Setting up after the connections are all in place, as it may use some of them)
317     layer_manager = new Inkscape::LayerManager( this );
319     showGrids(namedview->grids_visible, false);
321     temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
322     snapindicator = new Inkscape::Display::SnapIndicator ( this );
326 void SPDesktop::destroy()
328     if (snapindicator) {
329         delete snapindicator;
330         snapindicator = NULL;
331     }
332     if (temporary_item_list) {
333         delete temporary_item_list;
334         temporary_item_list = NULL;
335     }
337     namedview->hide(this);
339     _activate_connection.disconnect();
340     _deactivate_connection.disconnect();
341     _sel_modified_connection.disconnect();
342     _sel_changed_connection.disconnect();
343     _modified_connection.disconnect();
344     _commit_connection.disconnect();
345     _reconstruction_start_connection.disconnect();
346     _reconstruction_finish_connection.disconnect();
348     g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
349     g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
350     g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
352     while (event_context) {
353         SPEventContext *ec = event_context;
354         event_context = ec->next;
355         sp_event_context_finish (ec);
356         g_object_unref (G_OBJECT (ec));
357     }
359     if (_layer_hierarchy) {
360         delete _layer_hierarchy;
361 //        _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
362     }
364     if (layer_manager) {
365         delete layer_manager;
366         layer_manager = NULL;
367     }
369     if (_inkscape) {
370         _inkscape = NULL;
371     }
373     if (drawing) {
374         sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
375         drawing = NULL;
376     }
378     delete _guides_message_context;
379     _guides_message_context = NULL;
381     g_list_free (zooms_past);
382     g_list_free (zooms_future);
385 SPDesktop::~SPDesktop() {}
387 //--------------------------------------------------------------------
388 /* Public methods */
391 /** Note that lifetime is measured in milliseconds
392 * it is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
393 * The return value should only be used as argument for SPDesktop::remove_temporary_canvasitem, because the object might be deleted already.
394 */
395 Inkscape::Display::TemporaryItem *
396 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime)
398     return temporary_item_list->add_item(item, lifetime);
401 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
402 */
403 void
404 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
406     // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
407     if (tempitem && temporary_item_list) {
408         temporary_item_list->delete_item(tempitem);
409     }
412 void SPDesktop::setDisplayModeNormal()
414     SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
415     canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
416     displayMode = RENDERMODE_NORMAL;
417     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
418     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
421 void SPDesktop::setDisplayModeOutline()
423     SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
424     canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
425     displayMode = RENDERMODE_OUTLINE;
426     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
427     _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
430 void SPDesktop::displayModeToggle()
432     if (displayMode == RENDERMODE_OUTLINE)
433         setDisplayModeNormal();
434     else
435         setDisplayModeOutline();
438 /**
439  * Returns current root (=bottom) layer.
440  */
441 SPObject *SPDesktop::currentRoot() const
443     return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
446 /**
447  * Returns current top layer.
448  */
449 SPObject *SPDesktop::currentLayer() const
451     return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
454 /**
455  * Sets the current layer of the desktop.
456  *
457  * Make \a object the top layer.
458  */
459 void SPDesktop::setCurrentLayer(SPObject *object) {
460     g_return_if_fail(SP_IS_GROUP(object));
461     g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
462     // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
463     _layer_hierarchy->setBottom(object);
466 /**
467  * Return layer that contains \a object.
468  */
469 SPObject *SPDesktop::layerForObject(SPObject *object) {
470     g_return_val_if_fail(object != NULL, NULL);
472     SPObject *root=currentRoot();
473     object = SP_OBJECT_PARENT(object);
474     while ( object && object != root && !isLayer(object) ) {
475         object = SP_OBJECT_PARENT(object);
476     }
477     return object;
480 /**
481  * True if object is a layer.
482  */
483 bool SPDesktop::isLayer(SPObject *object) const {
484     return ( SP_IS_GROUP(object)
485              && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
486                   == SPGroup::LAYER ) );
489 /**
490  * True if desktop viewport fully contains \a item's bbox.
491  */
492 bool SPDesktop::isWithinViewport (SPItem *item) const
494     NR::Rect const viewport = get_display_area();
495     NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
496     if (bbox) {
497         return viewport.contains(*bbox);
498     } else {
499         return true;
500     }
503 ///
504 bool SPDesktop::itemIsHidden(SPItem const *item) const {
505     return item->isHidden(this->dkey);
508 /**
509  * Set activate property of desktop; emit signal if changed.
510  */
511 void
512 SPDesktop::set_active (bool new_active)
514     if (new_active != _active) {
515         _active = new_active;
516         if (new_active) {
517             _activate_signal.emit();
518         } else {
519             _deactivate_signal.emit();
520         }
521     }
524 /**
525  * Set activate status of current desktop's named view.
526  */
527 void
528 SPDesktop::activate_guides(bool activate)
530     guides_active = activate;
531     namedview->activateGuides(this, activate);
534 /**
535  * Make desktop switch documents.
536  */
537 void
538 SPDesktop::change_document (SPDocument *theDocument)
540     g_return_if_fail (theDocument != NULL);
542     /* unselect everything before switching documents */
543     selection->clear();
545     setDocument (theDocument);
547     /* update the rulers, connect the desktop widget's signal to the new namedview etc.
548        (this can probably be done in a better way) */
549     Gtk::Window *parent = this->getToplevel();
550     g_assert(parent != NULL);
551     SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
552     if (dtw) dtw->desktop = this;
553     sp_desktop_widget_update_namedview(dtw);
555     _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
556     _document_replaced_signal.emit (this, theDocument);
559 /**
560  * Make desktop switch event contexts.
561  */
562 void
563 SPDesktop::set_event_context (GtkType type, const gchar *config)
565     SPEventContext *ec;
566     while (event_context) {
567         ec = event_context;
568         sp_event_context_deactivate (ec);
569         event_context = ec->next;
570         sp_event_context_finish (ec);
571         g_object_unref (G_OBJECT (ec));
572     }
574     Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
575     ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
576     ec->next = event_context;
577     event_context = ec;
578     sp_event_context_activate (ec);
579     _event_context_changed_signal.emit (this, ec);
582 /**
583  * Push event context onto desktop's context stack.
584  */
585 void
586 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
588     SPEventContext *ref, *ec;
589     Inkscape::XML::Node *repr;
591     if (event_context && event_context->key == key) return;
592     ref = event_context;
593     while (ref && ref->next && ref->next->key != key) ref = ref->next;
594     if (ref && ref->next) {
595         ec = ref->next;
596         ref->next = ec->next;
597         sp_event_context_finish (ec);
598         g_object_unref (G_OBJECT (ec));
599     }
601     if (event_context) sp_event_context_deactivate (event_context);
602     repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
603     ec = sp_event_context_new (type, this, repr, key);
604     ec->next = event_context;
605     event_context = ec;
606     sp_event_context_activate (ec);
607     _event_context_changed_signal.emit (this, ec);
610 /**
611  * Sets the coordinate status to a given point
612  */
613 void
614 SPDesktop::set_coordinate_status (NR::Point p) {
615     _widget->setCoordinateStatus(p);
618 /**
619  * \see sp_document_item_from_list_at_point_bottom()
620  */
621 SPItem *
622 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
624     g_return_val_if_fail (doc() != NULL, NULL);
625     return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
628 /**
629  * \see sp_document_item_at_point()
630  */
631 SPItem *
632 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
634     g_return_val_if_fail (doc() != NULL, NULL);
635     return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
638 /**
639  * \see sp_document_group_at_point()
640  */
641 SPItem *
642 SPDesktop::group_at_point (NR::Point const p) const
644     g_return_val_if_fail (doc() != NULL, NULL);
645     return sp_document_group_at_point (doc(), dkey, p);
648 /**
649  * \brief  Returns the mouse point in document coordinates; if mouse is
650  * outside the canvas, returns the center of canvas viewpoint
651  */
652 NR::Point
653 SPDesktop::point() const
655     NR::Point p = _widget->getPointer();
656     NR::Point pw = sp_canvas_window_to_world (canvas, p);
657     p = w2d(pw);
659     NR::Rect const r = canvas->getViewbox();
661     NR::Point r0 = w2d(r.min());
662     NR::Point r1 = w2d(r.max());
664     if (p[NR::X] >= r0[NR::X] &&
665         p[NR::X] <= r1[NR::X] &&
666         p[NR::Y] >= r1[NR::Y] &&
667         p[NR::Y] <= r0[NR::Y])
668     {
669         return p;
670     } else {
671         return (r0 + r1) / 2;
672     }
675 /**
676  * Put current zoom data in history list.
677  */
678 void
679 SPDesktop::push_current_zoom (GList **history)
681     NR::Rect const area = get_display_area();
683     NRRect *old_zoom = g_new(NRRect, 1);
684     old_zoom->x0 = area.min()[NR::X];
685     old_zoom->x1 = area.max()[NR::X];
686     old_zoom->y0 = area.min()[NR::Y];
687     old_zoom->y1 = area.max()[NR::Y];
688     if ( *history == NULL
689          || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
690                ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
691                ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
692                ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
693     {
694         *history = g_list_prepend (*history, old_zoom);
695     }
698 /**
699  * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
700  */
701 void
702 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
704     g_assert(_widget);
706     // save the zoom
707     if (log) {
708         push_current_zoom(&zooms_past);
709         // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
710         g_list_free (zooms_future);
711         zooms_future = NULL;
712     }
714     double const cx = 0.5 * (x0 + x1);
715     double const cy = 0.5 * (y0 + y1);
717     NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
719     double scale = expansion(_d2w);
720     double newscale;
721     if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
722         newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
723     } else {
724         newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
725     }
727     newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
729     int clear = FALSE;
730     if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
731         /* Set zoom factors */
732         _d2w = NR::Matrix(NR::scale(newscale, -newscale));
733         _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
734         sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
735         clear = TRUE;
736     }
738     /* Calculate top left corner (in document pixels) */
739     x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
740     y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
742     /* Scroll */
743     sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
745     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
746     sp_box3d_context_update_lines(event_context);
748     _widget->updateRulers();
749     _widget->updateScrollbars(expansion(_d2w));
750     _widget->updateZoom();
753 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
755     set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
758 /**
759  * Return viewbox dimensions.
760  */
761 NR::Rect SPDesktop::get_display_area() const
763     NR::Rect const viewbox = canvas->getViewbox();
765     double const scale = _d2w[0];
767     return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
768                     NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
771 /**
772  * Revert back to previous zoom if possible.
773  */
774 void
775 SPDesktop::prev_zoom()
777     if (zooms_past == NULL) {
778         messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
779         return;
780     }
782     // push current zoom into forward zooms list
783     push_current_zoom (&zooms_future);
785     // restore previous zoom
786     set_display_area (((NRRect *) zooms_past->data)->x0,
787             ((NRRect *) zooms_past->data)->y0,
788             ((NRRect *) zooms_past->data)->x1,
789             ((NRRect *) zooms_past->data)->y1,
790             0, false);
792     // remove the just-added zoom from the past zooms list
793     zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
796 /**
797  * Set zoom to next in list.
798  */
799 void
800 SPDesktop::next_zoom()
802     if (zooms_future == NULL) {
803         this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
804         return;
805     }
807     // push current zoom into past zooms list
808     push_current_zoom (&zooms_past);
810     // restore next zoom
811     set_display_area (((NRRect *) zooms_future->data)->x0,
812             ((NRRect *) zooms_future->data)->y0,
813             ((NRRect *) zooms_future->data)->x1,
814             ((NRRect *) zooms_future->data)->y1,
815             0, false);
817     // remove the just-used zoom from the zooms_future list
818     zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
821 /**
822  * Zoom to point with absolute zoom factor.
823  */
824 void
825 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
827     zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
829     // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
830     // this check prevents "sliding" when trying to zoom in at maximum zoom;
831     /// \todo someone please fix calculations properly and remove this hack
832     if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
833         return;
835     NR::Rect const viewbox = canvas->getViewbox();
837     double const width2 = viewbox.dimensions()[NR::X] / zoom;
838     double const height2 = viewbox.dimensions()[NR::Y] / zoom;
840     set_display_area(cx - px * width2,
841                      cy - py * height2,
842                      cx + (1 - px) * width2,
843                      cy + (1 - py) * height2,
844                      0.0);
847 /**
848  * Zoom to center with absolute zoom factor.
849  */
850 void
851 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
853     zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
856 /**
857  * Zoom to point with relative zoom factor.
858  */
859 void
860 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
862     NR::Rect const area = get_display_area();
864     if (cx < area.min()[NR::X]) {
865         cx = area.min()[NR::X];
866     }
867     if (cx > area.max()[NR::X]) {
868         cx = area.max()[NR::X];
869     }
870     if (cy < area.min()[NR::Y]) {
871         cy = area.min()[NR::Y];
872     }
873     if (cy > area.max()[NR::Y]) {
874         cy = area.max()[NR::Y];
875     }
877     gdouble const scale = expansion(_d2w) * zoom;
878     double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
879     double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
881     zoom_absolute_keep_point(cx, cy, px, py, scale);
884 /**
885  * Zoom to center with relative zoom factor.
886  */
887 void
888 SPDesktop::zoom_relative (double cx, double cy, double zoom)
890     gdouble scale = expansion(_d2w) * zoom;
891     zoom_absolute (cx, cy, scale);
894 /**
895  * Set display area to origin and current document dimensions.
896  */
897 void
898 SPDesktop::zoom_page()
900     NR::Rect d(NR::Point(0, 0),
901                NR::Point(sp_document_width(doc()), sp_document_height(doc())));
903     if (d.isEmpty(1.0)) {
904         return;
905     }
907     set_display_area(d, 10);
910 /**
911  * Set display area to current document width.
912  */
913 void
914 SPDesktop::zoom_page_width()
916     NR::Rect const a = get_display_area();
918     if (sp_document_width(doc()) < 1.0) {
919         return;
920     }
922     NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
923                NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
925     set_display_area(d, 10);
928 /**
929  * Zoom to selection.
930  */
931 void
932 SPDesktop::zoom_selection()
934     NR::Maybe<NR::Rect> const d = selection->bounds();
936     if ( !d || d->isEmpty(0.1) ) {
937         return;
938     }
940     set_display_area(*d, 10);
943 /**
944  * Tell widget to let zoom widget grab keyboard focus.
945  */
946 void
947 SPDesktop::zoom_grab_focus()
949     _widget->letZoomGrabFocus();
952 /**
953  * Zoom to whole drawing.
954  */
955 void
956 SPDesktop::zoom_drawing()
958     g_return_if_fail (doc() != NULL);
959     SPItem *docitem = SP_ITEM (sp_document_root (doc()));
960     g_return_if_fail (docitem != NULL);
962     NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
964     /* Note that the second condition here indicates that
965     ** there are no items in the drawing.
966     */
967     if ( !d || d->isEmpty(1.0) ) {
968         return;
969     }
971     set_display_area(*d, 10);
974 /**
975  * Scroll canvas by specific coordinate amount.
976  */
977 void
978 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
980     g_assert(_widget);
982     NR::Rect const viewbox = canvas->getViewbox();
984     sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
986     /*  update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
987     sp_box3d_context_update_lines(event_context);
989     _widget->updateRulers();
990     _widget->updateScrollbars(expansion(_d2w));
993 bool
994 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
996     gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
998     // autoscrolldistance is in screen pixels, but the display area is in document units
999     autoscrolldistance /= expansion(_d2w);
1000     NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
1002     if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
1003         !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y])   ) {
1005         NR::Point const s_w( (*p) * _d2w );
1007         gdouble x_to;
1008         if ((*p)[NR::X] < dbox.min()[NR::X])
1009             x_to = dbox.min()[NR::X];
1010         else if ((*p)[NR::X] > dbox.max()[NR::X])
1011             x_to = dbox.max()[NR::X];
1012         else
1013             x_to = (*p)[NR::X];
1015         gdouble y_to;
1016         if ((*p)[NR::Y] < dbox.min()[NR::Y])
1017             y_to = dbox.min()[NR::Y];
1018         else if ((*p)[NR::Y] > dbox.max()[NR::Y])
1019             y_to = dbox.max()[NR::Y];
1020         else
1021             y_to = (*p)[NR::Y];
1023         NR::Point const d_dt(x_to, y_to);
1024         NR::Point const d_w( d_dt * _d2w );
1025         NR::Point const moved_w( d_w - s_w );
1027         if (autoscrollspeed == 0)
1028             autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
1030         if (autoscrollspeed != 0)
1031             scroll_world (autoscrollspeed * moved_w);
1033         return true;
1034     }
1035     return false;
1038 bool
1039 SPDesktop::is_iconified()
1041     return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1044 void
1045 SPDesktop::iconify()
1047     _widget->setIconified();
1050 bool
1051 SPDesktop::is_maximized()
1053     return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1056 void
1057 SPDesktop::maximize()
1059     _widget->setMaximized();
1062 bool
1063 SPDesktop::is_fullscreen()
1065     return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1068 void
1069 SPDesktop::fullscreen()
1071     _widget->setFullscreen();
1074 void
1075 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1077     _widget->getGeometry (x, y, w, h);
1080 void
1081 SPDesktop::setWindowPosition (NR::Point p)
1083     _widget->setPosition (p);
1086 void
1087 SPDesktop::setWindowSize (gint w, gint h)
1089     _widget->setSize (w, h);
1092 void
1093 SPDesktop::setWindowTransient (void *p, int transient_policy)
1095     _widget->setTransient (p, transient_policy);
1098 Gtk::Window*
1099 SPDesktop::getToplevel( )
1101     return _widget->getWindow();
1104 void
1105 SPDesktop::presentWindow()
1107     _widget->present();
1110 bool
1111 SPDesktop::warnDialog (gchar *text)
1113     return _widget->warnDialog (text);
1116 void
1117 SPDesktop::toggleRulers()
1119     _widget->toggleRulers();
1122 void
1123 SPDesktop::toggleScrollbars()
1125     _widget->toggleScrollbars();
1128 void
1129 SPDesktop::layoutWidget()
1131     _widget->layout();
1134 void
1135 SPDesktop::destroyWidget()
1137     _widget->destroy();
1140 bool
1141 SPDesktop::shutdown()
1143     return _widget->shutdown();
1146 bool SPDesktop::onDeleteUI (GdkEventAny*)
1148     if(shutdown()) 
1149         return true;
1151     destroyWidget();
1152     return false;
1155 /**
1156  *  onWindowStateEvent
1157  *
1158  *  Called when the window changes its maximize/fullscreen/iconify/pinned state.
1159  *  Since GTK doesn't have a way to query this state information directly, we
1160  *  record it for the desktop here, and also possibly trigger a layout.
1161  */
1162 bool
1163 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1165         // Record the desktop window's state
1166     window_state = event->new_window_state;
1168     // Layout may differ depending on full-screen mode or not
1169     GdkWindowState changed = event->changed_mask;
1170     if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1171         layoutWidget();
1172     }
1174         return false;
1177 void
1178 SPDesktop::setToolboxFocusTo (gchar const *label)
1180     _widget->setToolboxFocusTo (label);
1183 void
1184 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1186     _widget->setToolboxAdjustmentValue (id, val);
1189 void
1190 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1192     _widget->setToolboxSelectOneValue (id, val);
1195 bool
1196 SPDesktop::isToolboxButtonActive (gchar const *id)
1198     return _widget->isToolboxButtonActive (id);
1201 void
1202 SPDesktop::emitToolSubselectionChanged(gpointer data)
1204         _tool_subselection_changed.emit(data);
1205         inkscape_subselection_changed (this);
1208 void
1209 SPDesktop::updateNow()
1211   sp_canvas_update_now(canvas);
1214 void
1215 SPDesktop::enableInteraction()
1217   _widget->enableInteraction();
1220 void SPDesktop::disableInteraction()
1222   _widget->disableInteraction();
1225 void SPDesktop::setWaitingCursor()
1227     GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1228     gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1229     gdk_cursor_unref(waiting);
1230     waiting_cursor = true;
1232     // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1233     // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1234     // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1235     // after the call to setWaitingCursor as it was before
1236     while( Gtk::Main::events_pending() )
1237        Gtk::Main::iteration();
1240 void SPDesktop::clearWaitingCursor()
1242   if (waiting_cursor)
1243       sp_event_context_update_cursor(sp_desktop_event_context(this));
1246 void SPDesktop::toggleColorProfAdjust()
1248     _widget->toggleColorProfAdjust();
1251 void SPDesktop::toggleGrids()
1253     if (namedview->grids) {
1254         if(gridgroup) {
1255             showGrids(!grids_visible);
1256         }
1257     } else {
1258         //there is no grid present at the moment. add a rectangular grid and make it visible
1259         Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1260         Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1261         showGrids(true);
1262     }
1265 void SPDesktop::showGrids(bool show, bool dirty_document)
1267     grids_visible = show;
1268     sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1269     if (show) {
1270         sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1271     } else {
1272         sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1273     }
1276 void SPDesktop::toggleSnapping()
1278     bool v = namedview->snap_manager.getSnapEnabledGlobally();
1279     Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1280     sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1283 //----------------------------------------------------------------------
1284 // Callback implementations. The virtual ones are connected by the view.
1286 void
1287 SPDesktop::onPositionSet (double x, double y)
1289     _widget->viewSetPosition (NR::Point(x,y));
1292 void
1293 SPDesktop::onResized (double /*x*/, double /*y*/)
1295    // Nothing called here
1298 /**
1299  * Redraw callback; queues Gtk redraw; connected by View.
1300  */
1301 void
1302 SPDesktop::onRedrawRequested ()
1304     if (main) {
1305         _widget->requestCanvasUpdate();
1306     }
1309 void
1310 SPDesktop::updateCanvasNow()
1312   _widget->requestCanvasUpdateAndWait();
1315 /**
1316  * Associate document with desktop.
1317  */
1318 void
1319 SPDesktop::setDocument (SPDocument *doc)
1321     if (this->doc() && doc) {
1322         namedview->hide(this);
1323         sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1324     }
1326     if (_layer_hierarchy) {
1327         _layer_hierarchy->clear();
1328         delete _layer_hierarchy;
1329     }
1330     _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1331     _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1332     _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1333     _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1334     _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1336     /* setup EventLog */
1337     event_log = new Inkscape::EventLog(doc);
1338     doc->addUndoObserver(*event_log);
1340     _commit_connection.disconnect();
1341     _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1343     /// \todo fixme: This condition exists to make sure the code
1344     /// inside is NOT called on initialization, only on replacement. But there
1345     /// are surely more safe methods to accomplish this.
1346     // TODO since the comment had reversed logic, check the intent of this block of code:
1347     if (drawing) {
1348         NRArenaItem *ai = 0;
1350         namedview = sp_document_namedview (doc, NULL);
1351         _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1352         number = namedview->getViewCount();
1354         ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1355                 SP_CANVAS_ARENA (drawing)->arena,
1356                 dkey,
1357                 SP_ITEM_SHOW_DISPLAY);
1358         if (ai) {
1359             nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1360             nr_arena_item_unref (ai);
1361         }
1362         namedview->show(this);
1363         /* Ugly hack */
1364         activate_guides (true);
1365         /* Ugly hack */
1366         _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1367     }
1369     _document_replaced_signal.emit (this, doc);
1371     View::setDocument (doc);
1374 void
1375 SPDesktop::onStatusMessage
1376 (Inkscape::MessageType type, gchar const *message)
1378     if (_widget) {
1379         _widget->setMessage(type, message);
1380     }
1383 void
1384 SPDesktop::onDocumentURISet (gchar const* uri)
1386     _widget->setTitle(uri);
1389 /**
1390  * Resized callback.
1391  */
1392 void
1393 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1395     _doc2dt[5] = height;
1396     sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1397     NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1398     SP_CTRLRECT(page)->setRectangle(a);
1399     SP_CTRLRECT(page_border)->setRectangle(a);
1403 void
1404 SPDesktop::_onActivate (SPDesktop* dt)
1406     if (!dt->_widget) return;
1407     dt->_widget->activateDesktop();
1410 void
1411 SPDesktop::_onDeactivate (SPDesktop* dt)
1413     if (!dt->_widget) return;
1414     dt->_widget->deactivateDesktop();
1417 void
1418 SPDesktop::_onSelectionModified
1419 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1421     if (!dt->_widget) return;
1422     dt->_widget->updateScrollbars (expansion(dt->_d2w));
1425 static void
1426 _onSelectionChanged
1427 (Inkscape::Selection *selection, SPDesktop *desktop)
1429     /** \todo
1430      * only change the layer for single selections, or what?
1431      * This seems reasonable -- for multiple selections there can be many
1432      * different layers involved.
1433      */
1434     SPItem *item=selection->singleItem();
1435     if (item) {
1436         SPObject *layer=desktop->layerForObject(item);
1437         if ( layer && layer != desktop->currentLayer() ) {
1438             desktop->setCurrentLayer(layer);
1439         }
1440     }
1443 /**
1444  * Calls event handler of current event context.
1445  * \param arena Unused
1446  * \todo fixme
1447  */
1448 static gint
1449 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1451     if (ai) {
1452         SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1453         return sp_event_context_item_handler (desktop->event_context, spi, event);
1454     } else {
1455         return sp_event_context_root_handler (desktop->event_context, event);
1456     }
1459 static void
1460 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1461     g_return_if_fail(SP_IS_GROUP(layer));
1462     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1465 /// Callback
1466 static void
1467 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1468     g_return_if_fail(SP_IS_GROUP(layer));
1469     SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1472 /// Callback
1473 static void
1474 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1475                                          SPDesktop *desktop)
1477     desktop->_layer_changed_signal.emit (bottom);
1480 /// Called when document is starting to be rebuilt.
1481 static void
1482 _reconstruction_start (SPDesktop * desktop)
1484     // printf("Desktop, starting reconstruction\n");
1485     desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1486     desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1488     /*
1489     GSList const * selection_objs = desktop->selection->list();
1490     for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1492     }
1493     */
1494     desktop->selection->clear();
1496     // printf("Desktop, starting reconstruction end\n");
1499 /// Called when document rebuild is finished.
1500 static void
1501 _reconstruction_finish (SPDesktop * desktop)
1503     // printf("Desktop, finishing reconstruction\n");
1504     if (desktop->_reconstruction_old_layer_id == NULL)
1505         return;
1507     SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1508     if (newLayer != NULL)
1509         desktop->setCurrentLayer(newLayer);
1511     g_free(desktop->_reconstruction_old_layer_id);
1512     desktop->_reconstruction_old_layer_id = NULL;
1513     // printf("Desktop, finishing reconstruction end\n");
1514     return;
1517 /**
1518  * Namedview_modified callback.
1519  */
1520 static void
1521 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1523     SPNamedView *nv=SP_NAMEDVIEW(obj);
1525     if (flags & SP_OBJECT_MODIFIED_FLAG) {
1527         /* Recalculate snap distances */
1528         /* FIXME: why is the desktop getting involved in setting up something
1529         ** that is entirely to do with the namedview?
1530         */
1531         _update_snap_distances (desktop);
1533         /* Show/hide page background */
1534         if (nv->pagecolor & 0xff) {
1535             sp_canvas_item_show (desktop->table);
1536             ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1537             sp_canvas_item_move_to_z (desktop->table, 0);
1538         } else {
1539             sp_canvas_item_hide (desktop->table);
1540         }
1542         /* Show/hide page border */
1543         if (nv->showborder) {
1544             // show
1545             sp_canvas_item_show (desktop->page_border);
1546             // set color and shadow
1547             ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1548             if (nv->pageshadow) {
1549                 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1550             }
1551             // place in the z-order stack
1552             if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1553                  sp_canvas_item_move_to_z (desktop->page_border, 2);
1554             } else {
1555                 int order = sp_canvas_item_order (desktop->page_border);
1556                 int morder = sp_canvas_item_order (desktop->drawing);
1557                 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1558                                     morder - order);
1559             }
1560         } else {
1561                 sp_canvas_item_hide (desktop->page_border);
1562                 if (nv->pageshadow) {
1563                     ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1564                 }
1565         }
1567         /* Show/hide page shadow */
1568         if (nv->showpageshadow && nv->pageshadow) {
1569             ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1570         } else {
1571             ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1572         }
1574         if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1575             (SP_RGBA32_R_U(nv->pagecolor) +
1576              SP_RGBA32_G_U(nv->pagecolor) +
1577              SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1578             // the background color is light or transparent, use black outline
1579             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1580         } else { // use white outline
1581             SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1582         }
1583     }
1586 /**
1587  * Callback to reset snapper's distances.
1588  */
1589 static void
1590 _update_snap_distances (SPDesktop *desktop)
1592     SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1594     SPNamedView &nv = *desktop->namedview;
1596     //tell all grid snappers
1597     for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1598         Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1599         grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
1600                                                                       *nv.gridtoleranceunit,
1601                                                                       px));
1602     }
1604     nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
1605                                                                        *nv.guidetoleranceunit,
1606                                                                        px));
1607     nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
1608                                                                         *nv.objecttoleranceunit,
1609                                                                         px));
1613 NR::Matrix SPDesktop::w2d() const
1615     return _w2d;
1618 NR::Point SPDesktop::w2d(NR::Point const &p) const
1620     return p * _w2d;
1623 NR::Point SPDesktop::d2w(NR::Point const &p) const
1625     return p * _d2w;
1628 NR::Matrix SPDesktop::doc2dt() const
1630     return _doc2dt;
1633 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1635     return p * _doc2dt;
1638 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1640     return p / _doc2dt;
1644 /**
1645  * Pop event context from desktop's context stack. Never used.
1646  */
1647 // void
1648 // SPDesktop::pop_event_context (unsigned int key)
1649 // {
1650 //    SPEventContext *ec = NULL;
1651 //
1652 //    if (event_context && event_context->key == key) {
1653 //        g_return_if_fail (event_context);
1654 //        g_return_if_fail (event_context->next);
1655 //        ec = event_context;
1656 //        sp_event_context_deactivate (ec);
1657 //        event_context = ec->next;
1658 //        sp_event_context_activate (event_context);
1659 //        _event_context_changed_signal.emit (this, ec);
1660 //    }
1661 //
1662 //    SPEventContext *ref = event_context;
1663 //    while (ref && ref->next && ref->next->key != key)
1664 //        ref = ref->next;
1665 //
1666 //    if (ref && ref->next) {
1667 //        ec = ref->next;
1668 //        ref->next = ec->next;
1669 //    }
1670 //
1671 //    if (ec) {
1672 //        sp_event_context_finish (ec);
1673 //        g_object_unref (G_OBJECT (ec));
1674 //    }
1675 // }
1677 /*
1678   Local Variables:
1679   mode:c++
1680   c-file-style:"stroustrup"
1681   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1682   indent-tabs-mode:nil
1683   fill-column:99
1684   End:
1685 */
1686 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :