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-2007 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 "libnr/nr-matrix-div.h"
81 #include "libnr/nr-rect-ops.h"
82 #include "ui/dialog/dialog-manager.h"
83 #include "xml/repr.h"
84 #include "message-context.h"
85 #include "layer-manager.h"
86 #include "event-log.h"
87 #include "display/canvas-grid.h"
88 #include "widgets/desktop-widget.h"
90 #include "display/sp-canvas.h"
92 namespace Inkscape { namespace XML { class Node; }}
94 // Callback declarations
95 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
96 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
97 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
98 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
99 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
100 static void _reconstruction_start(SPDesktop * desktop);
101 static void _reconstruction_finish(SPDesktop * desktop);
102 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
103 static void _update_snap_distances (SPDesktop *desktop);
105 /**
106 * Return new desktop object.
107 * \pre namedview != NULL.
108 * \pre canvas != NULL.
109 */
110 SPDesktop::SPDesktop() :
111 _dlg_mgr( 0 ),
112 namedview( 0 ),
113 canvas( 0 ),
114 selection( 0 ),
115 event_context( 0 ),
116 layer_manager( 0 ),
117 event_log( 0 ),
118 acetate( 0 ),
119 main( 0 ),
120 gridgroup( 0 ),
121 guides( 0 ),
122 drawing( 0 ),
123 sketch( 0 ),
124 controls( 0 ),
125 table( 0 ),
126 page( 0 ),
127 page_border( 0 ),
128 current( 0 ),
129 zooms_past( 0 ),
130 zooms_future( 0 ),
131 dkey( 0 ),
132 number( 0 ),
133 window_state(0),
134 interaction_disabled_counter( 0 ),
135 waiting_cursor( false ),
136 guides_active( false ),
137 gr_item( 0 ),
138 gr_point_type( 0 ),
139 gr_point_i( 0 ),
140 gr_fill_or_stroke( true ),
141 _layer_hierarchy( 0 ),
142 _reconstruction_old_layer_id( 0 ),
143 _widget( 0 ),
144 _inkscape( 0 ),
145 _guides_message_context( 0 ),
146 _active( false ),
147 _w2d(),
148 _d2w(),
149 _doc2dt( NR::Matrix(NR::scale(1, -1)) ),
150 grids_visible( false )
151 {
152 _d2w.set_identity();
153 _w2d.set_identity();
155 selection = Inkscape::GC::release( new Inkscape::Selection(this) );
156 }
158 void
159 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
160 {
161 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
163 current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
165 namedview = nv;
166 canvas = aCanvas;
168 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
169 /* Kill flicker */
170 sp_document_ensure_up_to_date (document);
172 /* Setup Dialog Manager */
173 _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
175 dkey = sp_item_display_key_new (1);
177 /* Connect document */
178 setDocument (document);
180 number = namedview->getViewCount();
183 /* Setup Canvas */
184 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
186 SPCanvasGroup *root = sp_canvas_root (canvas);
188 /* Setup adminstrative layers */
189 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
190 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
191 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
192 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
194 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
195 SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
196 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
197 sp_canvas_item_move_to_z (table, 0);
199 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
200 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
201 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
203 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
204 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
206 SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
208 if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
209 // Start in outline mode
210 setDisplayModeOutline();
211 } else {
212 // Start in normal mode, default
213 setDisplayModeNormal();
214 }
216 gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
217 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
218 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
219 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
221 /* Push select tool to the bottom of stack */
222 /** \todo
223 * FIXME: this is the only call to this. Everything else seems to just
224 * call "set" instead of "push". Can we assume that there is only one
225 * context ever?
226 */
227 push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
229 // display rect and zoom are now handled in sp_desktop_widget_realize()
231 NR::Rect const d(NR::Point(0.0, 0.0),
232 NR::Point(sp_document_width(document), sp_document_height(document)));
234 SP_CTRLRECT(page)->setRectangle(d);
235 SP_CTRLRECT(page_border)->setRectangle(d);
237 /* the following sets the page shadow on the canvas
238 It was originally set to 5, which is really cheesy!
239 It now is an attribute in the document's namedview. If a value of
240 0 is used, then the constructor for a shadow is not initialized.
241 */
243 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
244 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
245 }
248 /* Connect event for page resize */
249 _doc2dt[5] = sp_document_height (document);
250 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
252 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
254 NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
255 SP_CANVAS_ARENA (drawing)->arena,
256 dkey,
257 SP_ITEM_SHOW_DISPLAY);
258 if (ai) {
259 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
260 nr_arena_item_unref (ai);
261 }
263 namedview->show(this);
264 /* Ugly hack */
265 activate_guides (true);
266 /* Ugly hack */
267 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
269 /* Set up notification of rebuilding the document, this allows
270 for saving object related settings in the document. */
271 _reconstruction_start_connection =
272 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
273 _reconstruction_finish_connection =
274 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
275 _reconstruction_old_layer_id = NULL;
277 // ?
278 // sp_active_desktop_set (desktop);
279 _inkscape = INKSCAPE;
281 _activate_connection = _activate_signal.connect(
282 sigc::bind(
283 sigc::ptr_fun(_onActivate),
284 this
285 )
286 );
287 _deactivate_connection = _deactivate_signal.connect(
288 sigc::bind(
289 sigc::ptr_fun(_onDeactivate),
290 this
291 )
292 );
294 _sel_modified_connection = selection->connectModified(
295 sigc::bind(
296 sigc::ptr_fun(&_onSelectionModified),
297 this
298 )
299 );
300 _sel_changed_connection = selection->connectChanged(
301 sigc::bind(
302 sigc::ptr_fun(&_onSelectionChanged),
303 this
304 )
305 );
308 /* setup LayerManager */
309 // (Setting up after the connections are all in place, as it may use some of them)
310 layer_manager = new Inkscape::LayerManager( this );
312 showGrids(namedview->grids_visible, false);
313 }
316 void SPDesktop::destroy()
317 {
318 namedview->hide(this);
320 _activate_connection.disconnect();
321 _deactivate_connection.disconnect();
322 _sel_modified_connection.disconnect();
323 _sel_changed_connection.disconnect();
324 _modified_connection.disconnect();
325 _commit_connection.disconnect();
326 _reconstruction_start_connection.disconnect();
327 _reconstruction_finish_connection.disconnect();
329 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
330 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
331 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
333 while (event_context) {
334 SPEventContext *ec = event_context;
335 event_context = ec->next;
336 sp_event_context_finish (ec);
337 g_object_unref (G_OBJECT (ec));
338 }
340 if (_layer_hierarchy) {
341 delete _layer_hierarchy;
342 // _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
343 }
345 if (layer_manager) {
346 delete layer_manager;
347 layer_manager = NULL;
348 }
350 if (_inkscape) {
351 _inkscape = NULL;
352 }
354 if (drawing) {
355 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
356 drawing = NULL;
357 }
359 delete _guides_message_context;
360 _guides_message_context = NULL;
362 g_list_free (zooms_past);
363 g_list_free (zooms_future);
364 }
366 SPDesktop::~SPDesktop() {}
368 //--------------------------------------------------------------------
369 /* Public methods */
371 void SPDesktop::setDisplayModeNormal()
372 {
373 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
374 canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
375 displayMode = RENDERMODE_NORMAL;
376 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
377 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
378 }
380 void SPDesktop::setDisplayModeOutline()
381 {
382 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
383 canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
384 displayMode = RENDERMODE_OUTLINE;
385 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
386 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
387 }
389 void SPDesktop::displayModeToggle()
390 {
391 if (displayMode == RENDERMODE_OUTLINE)
392 setDisplayModeNormal();
393 else
394 setDisplayModeOutline();
395 }
397 /**
398 * Returns current root (=bottom) layer.
399 */
400 SPObject *SPDesktop::currentRoot() const
401 {
402 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
403 }
405 /**
406 * Returns current top layer.
407 */
408 SPObject *SPDesktop::currentLayer() const
409 {
410 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
411 }
413 /**
414 * Sets the current layer of the desktop.
415 *
416 * Make \a object the top layer.
417 */
418 void SPDesktop::setCurrentLayer(SPObject *object) {
419 g_return_if_fail(SP_IS_GROUP(object));
420 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
421 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
422 _layer_hierarchy->setBottom(object);
423 }
425 /**
426 * Return layer that contains \a object.
427 */
428 SPObject *SPDesktop::layerForObject(SPObject *object) {
429 g_return_val_if_fail(object != NULL, NULL);
431 SPObject *root=currentRoot();
432 object = SP_OBJECT_PARENT(object);
433 while ( object && object != root && !isLayer(object) ) {
434 object = SP_OBJECT_PARENT(object);
435 }
436 return object;
437 }
439 /**
440 * True if object is a layer.
441 */
442 bool SPDesktop::isLayer(SPObject *object) const {
443 return ( SP_IS_GROUP(object)
444 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
445 == SPGroup::LAYER ) );
446 }
448 /**
449 * True if desktop viewport fully contains \a item's bbox.
450 */
451 bool SPDesktop::isWithinViewport (SPItem *item) const
452 {
453 NR::Rect const viewport = get_display_area();
454 NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
455 if (bbox) {
456 return viewport.contains(*bbox);
457 } else {
458 return true;
459 }
460 }
462 ///
463 bool SPDesktop::itemIsHidden(SPItem const *item) const {
464 return item->isHidden(this->dkey);
465 }
467 /**
468 * Set activate property of desktop; emit signal if changed.
469 */
470 void
471 SPDesktop::set_active (bool new_active)
472 {
473 if (new_active != _active) {
474 _active = new_active;
475 if (new_active) {
476 _activate_signal.emit();
477 } else {
478 _deactivate_signal.emit();
479 }
480 }
481 }
483 /**
484 * Set activate status of current desktop's named view.
485 */
486 void
487 SPDesktop::activate_guides(bool activate)
488 {
489 guides_active = activate;
490 namedview->activateGuides(this, activate);
491 }
493 /**
494 * Make desktop switch documents.
495 */
496 void
497 SPDesktop::change_document (SPDocument *theDocument)
498 {
499 g_return_if_fail (theDocument != NULL);
501 /* unselect everything before switching documents */
502 selection->clear();
504 setDocument (theDocument);
506 /* update the rulers, connect the desktop widget's signal to the new namedview etc.
507 (this can probably be done in a better way) */
508 Gtk::Window *parent = this->getToplevel();
509 g_assert(parent != NULL);
510 SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
511 if (dtw) dtw->desktop = this;
512 sp_desktop_widget_update_namedview(dtw);
514 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
515 _document_replaced_signal.emit (this, theDocument);
516 }
518 /**
519 * Make desktop switch event contexts.
520 */
521 void
522 SPDesktop::set_event_context (GtkType type, const gchar *config)
523 {
524 SPEventContext *ec;
525 while (event_context) {
526 ec = event_context;
527 sp_event_context_deactivate (ec);
528 event_context = ec->next;
529 sp_event_context_finish (ec);
530 g_object_unref (G_OBJECT (ec));
531 }
533 Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
534 ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
535 ec->next = event_context;
536 event_context = ec;
537 sp_event_context_activate (ec);
538 _event_context_changed_signal.emit (this, ec);
539 }
541 /**
542 * Push event context onto desktop's context stack.
543 */
544 void
545 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
546 {
547 SPEventContext *ref, *ec;
548 Inkscape::XML::Node *repr;
550 if (event_context && event_context->key == key) return;
551 ref = event_context;
552 while (ref && ref->next && ref->next->key != key) ref = ref->next;
553 if (ref && ref->next) {
554 ec = ref->next;
555 ref->next = ec->next;
556 sp_event_context_finish (ec);
557 g_object_unref (G_OBJECT (ec));
558 }
560 if (event_context) sp_event_context_deactivate (event_context);
561 repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
562 ec = sp_event_context_new (type, this, repr, key);
563 ec->next = event_context;
564 event_context = ec;
565 sp_event_context_activate (ec);
566 _event_context_changed_signal.emit (this, ec);
567 }
569 /**
570 * Sets the coordinate status to a given point
571 */
572 void
573 SPDesktop::set_coordinate_status (NR::Point p) {
574 _widget->setCoordinateStatus(p);
575 }
577 /**
578 * \see sp_document_item_from_list_at_point_bottom()
579 */
580 SPItem *
581 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
582 {
583 g_return_val_if_fail (doc() != NULL, NULL);
584 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
585 }
587 /**
588 * \see sp_document_item_at_point()
589 */
590 SPItem *
591 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
592 {
593 g_return_val_if_fail (doc() != NULL, NULL);
594 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
595 }
597 /**
598 * \see sp_document_group_at_point()
599 */
600 SPItem *
601 SPDesktop::group_at_point (NR::Point const p) const
602 {
603 g_return_val_if_fail (doc() != NULL, NULL);
604 return sp_document_group_at_point (doc(), dkey, p);
605 }
607 /**
608 * \brief Returns the mouse point in document coordinates; if mouse is
609 * outside the canvas, returns the center of canvas viewpoint
610 */
611 NR::Point
612 SPDesktop::point() const
613 {
614 NR::Point p = _widget->getPointer();
615 NR::Point pw = sp_canvas_window_to_world (canvas, p);
616 p = w2d(pw);
618 NR::Rect const r = canvas->getViewbox();
620 NR::Point r0 = w2d(r.min());
621 NR::Point r1 = w2d(r.max());
623 if (p[NR::X] >= r0[NR::X] &&
624 p[NR::X] <= r1[NR::X] &&
625 p[NR::Y] >= r1[NR::Y] &&
626 p[NR::Y] <= r0[NR::Y])
627 {
628 return p;
629 } else {
630 return (r0 + r1) / 2;
631 }
632 }
634 /**
635 * Put current zoom data in history list.
636 */
637 void
638 SPDesktop::push_current_zoom (GList **history)
639 {
640 NR::Rect const area = get_display_area();
642 NRRect *old_zoom = g_new(NRRect, 1);
643 old_zoom->x0 = area.min()[NR::X];
644 old_zoom->x1 = area.max()[NR::X];
645 old_zoom->y0 = area.min()[NR::Y];
646 old_zoom->y1 = area.max()[NR::Y];
647 if ( *history == NULL
648 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
649 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
650 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
651 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
652 {
653 *history = g_list_prepend (*history, old_zoom);
654 }
655 }
657 /**
658 * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
659 */
660 void
661 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
662 {
663 g_assert(_widget);
665 // save the zoom
666 if (log) {
667 push_current_zoom(&zooms_past);
668 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
669 g_list_free (zooms_future);
670 zooms_future = NULL;
671 }
673 double const cx = 0.5 * (x0 + x1);
674 double const cy = 0.5 * (y0 + y1);
676 NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
678 double scale = expansion(_d2w);
679 double newscale;
680 if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
681 newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
682 } else {
683 newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
684 }
686 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
688 int clear = FALSE;
689 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
690 /* Set zoom factors */
691 _d2w = NR::Matrix(NR::scale(newscale, -newscale));
692 _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
693 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
694 clear = TRUE;
695 }
697 /* Calculate top left corner (in document pixels) */
698 x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
699 y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
701 /* Scroll */
702 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
704 _widget->updateRulers();
705 _widget->updateScrollbars(expansion(_d2w));
706 _widget->updateZoom();
707 }
709 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
710 {
711 set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
712 }
714 /**
715 * Return viewbox dimensions.
716 */
717 NR::Rect SPDesktop::get_display_area() const
718 {
719 NR::Rect const viewbox = canvas->getViewbox();
721 double const scale = _d2w[0];
723 return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
724 NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
725 }
727 /**
728 * Revert back to previous zoom if possible.
729 */
730 void
731 SPDesktop::prev_zoom()
732 {
733 if (zooms_past == NULL) {
734 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
735 return;
736 }
738 // push current zoom into forward zooms list
739 push_current_zoom (&zooms_future);
741 // restore previous zoom
742 set_display_area (((NRRect *) zooms_past->data)->x0,
743 ((NRRect *) zooms_past->data)->y0,
744 ((NRRect *) zooms_past->data)->x1,
745 ((NRRect *) zooms_past->data)->y1,
746 0, false);
748 // remove the just-added zoom from the past zooms list
749 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
750 }
752 /**
753 * Set zoom to next in list.
754 */
755 void
756 SPDesktop::next_zoom()
757 {
758 if (zooms_future == NULL) {
759 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
760 return;
761 }
763 // push current zoom into past zooms list
764 push_current_zoom (&zooms_past);
766 // restore next zoom
767 set_display_area (((NRRect *) zooms_future->data)->x0,
768 ((NRRect *) zooms_future->data)->y0,
769 ((NRRect *) zooms_future->data)->x1,
770 ((NRRect *) zooms_future->data)->y1,
771 0, false);
773 // remove the just-used zoom from the zooms_future list
774 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
775 }
777 /**
778 * Zoom to point with absolute zoom factor.
779 */
780 void
781 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
782 {
783 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
785 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
786 // this check prevents "sliding" when trying to zoom in at maximum zoom;
787 /// \todo someone please fix calculations properly and remove this hack
788 if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
789 return;
791 NR::Rect const viewbox = canvas->getViewbox();
793 double const width2 = viewbox.dimensions()[NR::X] / zoom;
794 double const height2 = viewbox.dimensions()[NR::Y] / zoom;
796 set_display_area(cx - px * width2,
797 cy - py * height2,
798 cx + (1 - px) * width2,
799 cy + (1 - py) * height2,
800 0.0);
801 }
803 /**
804 * Zoom to center with absolute zoom factor.
805 */
806 void
807 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
808 {
809 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
810 }
812 /**
813 * Zoom to point with relative zoom factor.
814 */
815 void
816 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
817 {
818 NR::Rect const area = get_display_area();
820 if (cx < area.min()[NR::X]) {
821 cx = area.min()[NR::X];
822 }
823 if (cx > area.max()[NR::X]) {
824 cx = area.max()[NR::X];
825 }
826 if (cy < area.min()[NR::Y]) {
827 cy = area.min()[NR::Y];
828 }
829 if (cy > area.max()[NR::Y]) {
830 cy = area.max()[NR::Y];
831 }
833 gdouble const scale = expansion(_d2w) * zoom;
834 double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
835 double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
837 zoom_absolute_keep_point(cx, cy, px, py, scale);
838 }
840 /**
841 * Zoom to center with relative zoom factor.
842 */
843 void
844 SPDesktop::zoom_relative (double cx, double cy, double zoom)
845 {
846 gdouble scale = expansion(_d2w) * zoom;
847 zoom_absolute (cx, cy, scale);
848 }
850 /**
851 * Set display area to origin and current document dimensions.
852 */
853 void
854 SPDesktop::zoom_page()
855 {
856 NR::Rect d(NR::Point(0, 0),
857 NR::Point(sp_document_width(doc()), sp_document_height(doc())));
859 if (d.isEmpty(1.0)) {
860 return;
861 }
863 set_display_area(d, 10);
864 }
866 /**
867 * Set display area to current document width.
868 */
869 void
870 SPDesktop::zoom_page_width()
871 {
872 NR::Rect const a = get_display_area();
874 if (sp_document_width(doc()) < 1.0) {
875 return;
876 }
878 NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
879 NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
881 set_display_area(d, 10);
882 }
884 /**
885 * Zoom to selection.
886 */
887 void
888 SPDesktop::zoom_selection()
889 {
890 NR::Maybe<NR::Rect> const d = selection->bounds();
892 if ( !d || d->isEmpty(0.1) ) {
893 return;
894 }
896 set_display_area(*d, 10);
897 }
899 /**
900 * Tell widget to let zoom widget grab keyboard focus.
901 */
902 void
903 SPDesktop::zoom_grab_focus()
904 {
905 _widget->letZoomGrabFocus();
906 }
908 /**
909 * Zoom to whole drawing.
910 */
911 void
912 SPDesktop::zoom_drawing()
913 {
914 g_return_if_fail (doc() != NULL);
915 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
916 g_return_if_fail (docitem != NULL);
918 NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
920 /* Note that the second condition here indicates that
921 ** there are no items in the drawing.
922 */
923 if ( !d || d->isEmpty(1.0) ) {
924 return;
925 }
927 set_display_area(*d, 10);
928 }
930 /**
931 * Scroll canvas by specific coordinate amount.
932 */
933 void
934 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
935 {
936 g_assert(_widget);
938 NR::Rect const viewbox = canvas->getViewbox();
940 sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
942 _widget->updateRulers();
943 _widget->updateScrollbars(expansion(_d2w));
944 }
946 bool
947 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
948 {
949 gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
951 // autoscrolldistance is in screen pixels, but the display area is in document units
952 autoscrolldistance /= expansion(_d2w);
953 NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
955 if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
956 !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y]) ) {
958 NR::Point const s_w( (*p) * _d2w );
960 gdouble x_to;
961 if ((*p)[NR::X] < dbox.min()[NR::X])
962 x_to = dbox.min()[NR::X];
963 else if ((*p)[NR::X] > dbox.max()[NR::X])
964 x_to = dbox.max()[NR::X];
965 else
966 x_to = (*p)[NR::X];
968 gdouble y_to;
969 if ((*p)[NR::Y] < dbox.min()[NR::Y])
970 y_to = dbox.min()[NR::Y];
971 else if ((*p)[NR::Y] > dbox.max()[NR::Y])
972 y_to = dbox.max()[NR::Y];
973 else
974 y_to = (*p)[NR::Y];
976 NR::Point const d_dt(x_to, y_to);
977 NR::Point const d_w( d_dt * _d2w );
978 NR::Point const moved_w( d_w - s_w );
980 if (autoscrollspeed == 0)
981 autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
983 if (autoscrollspeed != 0)
984 scroll_world (autoscrollspeed * moved_w);
986 return true;
987 }
988 return false;
989 }
991 bool
992 SPDesktop::is_iconified()
993 {
994 return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
995 }
997 void
998 SPDesktop::iconify()
999 {
1000 _widget->setIconified();
1001 }
1003 bool
1004 SPDesktop::is_maximized()
1005 {
1006 return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1007 }
1009 void
1010 SPDesktop::maximize()
1011 {
1012 _widget->setMaximized();
1013 }
1015 bool
1016 SPDesktop::is_fullscreen()
1017 {
1018 return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1019 }
1021 void
1022 SPDesktop::fullscreen()
1023 {
1024 _widget->setFullscreen();
1025 }
1027 void
1028 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1029 {
1030 _widget->getGeometry (x, y, w, h);
1031 }
1033 void
1034 SPDesktop::setWindowPosition (NR::Point p)
1035 {
1036 _widget->setPosition (p);
1037 }
1039 void
1040 SPDesktop::setWindowSize (gint w, gint h)
1041 {
1042 _widget->setSize (w, h);
1043 }
1045 void
1046 SPDesktop::setWindowTransient (void *p, int transient_policy)
1047 {
1048 _widget->setTransient (p, transient_policy);
1049 }
1051 Gtk::Window*
1052 SPDesktop::getToplevel( )
1053 {
1054 return _widget->getWindow();
1055 }
1057 void
1058 SPDesktop::presentWindow()
1059 {
1060 _widget->present();
1061 }
1063 bool
1064 SPDesktop::warnDialog (gchar *text)
1065 {
1066 return _widget->warnDialog (text);
1067 }
1069 void
1070 SPDesktop::toggleRulers()
1071 {
1072 _widget->toggleRulers();
1073 }
1075 void
1076 SPDesktop::toggleScrollbars()
1077 {
1078 _widget->toggleScrollbars();
1079 }
1081 void
1082 SPDesktop::layoutWidget()
1083 {
1084 _widget->layout();
1085 }
1087 void
1088 SPDesktop::destroyWidget()
1089 {
1090 _widget->destroy();
1091 }
1093 bool
1094 SPDesktop::shutdown()
1095 {
1096 return _widget->shutdown();
1097 }
1099 bool SPDesktop::onDeleteUI (GdkEventAny*)
1100 {
1101 if(shutdown())
1102 return true;
1104 destroyWidget();
1105 return false;
1106 }
1108 /**
1109 * onWindowStateEvent
1110 *
1111 * Called when the window changes its maximize/fullscreen/iconify/pinned state.
1112 * Since GTK doesn't have a way to query this state information directly, we
1113 * record it for the desktop here, and also possibly trigger a layout.
1114 */
1115 bool
1116 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1117 {
1118 // Record the desktop window's state
1119 window_state = event->new_window_state;
1121 // Layout may differ depending on full-screen mode or not
1122 GdkWindowState changed = event->changed_mask;
1123 if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1124 layoutWidget();
1125 }
1127 return false;
1128 }
1130 void
1131 SPDesktop::setToolboxFocusTo (gchar const *label)
1132 {
1133 _widget->setToolboxFocusTo (label);
1134 }
1136 void
1137 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1138 {
1139 _widget->setToolboxAdjustmentValue (id, val);
1140 }
1142 void
1143 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1144 {
1145 _widget->setToolboxSelectOneValue (id, val);
1146 }
1148 bool
1149 SPDesktop::isToolboxButtonActive (gchar const *id)
1150 {
1151 return _widget->isToolboxButtonActive (id);
1152 }
1154 void
1155 SPDesktop::emitToolSubselectionChanged(gpointer data)
1156 {
1157 _tool_subselection_changed.emit(data);
1158 inkscape_subselection_changed (this);
1159 }
1161 void
1162 SPDesktop::updateNow()
1163 {
1164 sp_canvas_update_now(canvas);
1165 }
1167 void
1168 SPDesktop::enableInteraction()
1169 {
1170 _widget->enableInteraction();
1171 }
1173 void SPDesktop::disableInteraction()
1174 {
1175 _widget->disableInteraction();
1176 }
1178 void SPDesktop::setWaitingCursor()
1179 {
1180 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1181 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1182 gdk_cursor_unref(waiting);
1183 waiting_cursor = true;
1185 // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1186 // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1187 // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1188 // after the call to setWaitingCursor as it was before
1189 while( Gtk::Main::events_pending() )
1190 Gtk::Main::iteration();
1191 }
1193 void SPDesktop::clearWaitingCursor()
1194 {
1195 if (waiting_cursor)
1196 sp_event_context_update_cursor(sp_desktop_event_context(this));
1197 }
1199 void SPDesktop::toggleColorProfAdjust()
1200 {
1201 _widget->toggleColorProfAdjust();
1202 }
1204 void SPDesktop::toggleGrids()
1205 {
1206 if (namedview->grids) {
1207 if(gridgroup) {
1208 showGrids(!grids_visible);
1209 }
1210 } else {
1211 //there is no grid present at the moment. add a rectangular grid and make it visible
1212 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1213 Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1214 showGrids(true);
1215 }
1216 }
1218 void SPDesktop::showGrids(bool show, bool dirty_document)
1219 {
1220 grids_visible = show;
1221 sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1222 if (show) {
1223 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1224 } else {
1225 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1226 }
1227 }
1229 void SPDesktop::toggleSnapping()
1230 {
1231 bool v = namedview->snap_manager.getSnapEnabledGlobally();
1232 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1233 sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1234 }
1236 //----------------------------------------------------------------------
1237 // Callback implementations. The virtual ones are connected by the view.
1239 void
1240 SPDesktop::onPositionSet (double x, double y)
1241 {
1242 _widget->viewSetPosition (NR::Point(x,y));
1243 }
1245 void
1246 SPDesktop::onResized (double /*x*/, double /*y*/)
1247 {
1248 // Nothing called here
1249 }
1251 /**
1252 * Redraw callback; queues Gtk redraw; connected by View.
1253 */
1254 void
1255 SPDesktop::onRedrawRequested ()
1256 {
1257 if (main) {
1258 _widget->requestCanvasUpdate();
1259 }
1260 }
1262 void
1263 SPDesktop::updateCanvasNow()
1264 {
1265 _widget->requestCanvasUpdateAndWait();
1266 }
1268 /**
1269 * Associate document with desktop.
1270 */
1271 void
1272 SPDesktop::setDocument (SPDocument *doc)
1273 {
1274 if (this->doc() && doc) {
1275 namedview->hide(this);
1276 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1277 }
1279 if (_layer_hierarchy) {
1280 _layer_hierarchy->clear();
1281 delete _layer_hierarchy;
1282 }
1283 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1284 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1285 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1286 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1287 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1289 /* setup EventLog */
1290 event_log = new Inkscape::EventLog(doc);
1291 doc->addUndoObserver(*event_log);
1293 _commit_connection.disconnect();
1294 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1296 /// \todo fixme: This condition exists to make sure the code
1297 /// inside is NOT called on initialization, only on replacement. But there
1298 /// are surely more safe methods to accomplish this.
1299 // TODO since the comment had reversed logic, check the intent of this block of code:
1300 if (drawing) {
1301 NRArenaItem *ai = 0;
1303 namedview = sp_document_namedview (doc, NULL);
1304 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1305 number = namedview->getViewCount();
1307 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1308 SP_CANVAS_ARENA (drawing)->arena,
1309 dkey,
1310 SP_ITEM_SHOW_DISPLAY);
1311 if (ai) {
1312 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1313 nr_arena_item_unref (ai);
1314 }
1315 namedview->show(this);
1316 /* Ugly hack */
1317 activate_guides (true);
1318 /* Ugly hack */
1319 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1320 }
1322 _document_replaced_signal.emit (this, doc);
1324 View::setDocument (doc);
1325 }
1327 void
1328 SPDesktop::onStatusMessage
1329 (Inkscape::MessageType type, gchar const *message)
1330 {
1331 if (_widget) {
1332 _widget->setMessage(type, message);
1333 }
1334 }
1336 void
1337 SPDesktop::onDocumentURISet (gchar const* uri)
1338 {
1339 _widget->setTitle(uri);
1340 }
1342 /**
1343 * Resized callback.
1344 */
1345 void
1346 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1347 {
1348 _doc2dt[5] = height;
1349 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1350 NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1351 SP_CTRLRECT(page)->setRectangle(a);
1352 SP_CTRLRECT(page_border)->setRectangle(a);
1353 }
1356 void
1357 SPDesktop::_onActivate (SPDesktop* dt)
1358 {
1359 if (!dt->_widget) return;
1360 dt->_widget->activateDesktop();
1361 }
1363 void
1364 SPDesktop::_onDeactivate (SPDesktop* dt)
1365 {
1366 if (!dt->_widget) return;
1367 dt->_widget->deactivateDesktop();
1368 }
1370 void
1371 SPDesktop::_onSelectionModified
1372 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1373 {
1374 if (!dt->_widget) return;
1375 dt->_widget->updateScrollbars (expansion(dt->_d2w));
1376 }
1378 static void
1379 _onSelectionChanged
1380 (Inkscape::Selection *selection, SPDesktop *desktop)
1381 {
1382 /** \todo
1383 * only change the layer for single selections, or what?
1384 * This seems reasonable -- for multiple selections there can be many
1385 * different layers involved.
1386 */
1387 SPItem *item=selection->singleItem();
1388 if (item) {
1389 SPObject *layer=desktop->layerForObject(item);
1390 if ( layer && layer != desktop->currentLayer() ) {
1391 desktop->setCurrentLayer(layer);
1392 }
1393 }
1394 }
1396 /**
1397 * Calls event handler of current event context.
1398 * \param arena Unused
1399 * \todo fixme
1400 */
1401 static gint
1402 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1403 {
1404 if (ai) {
1405 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1406 return sp_event_context_item_handler (desktop->event_context, spi, event);
1407 } else {
1408 return sp_event_context_root_handler (desktop->event_context, event);
1409 }
1410 }
1412 static void
1413 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1414 g_return_if_fail(SP_IS_GROUP(layer));
1415 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1416 }
1418 /// Callback
1419 static void
1420 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1421 g_return_if_fail(SP_IS_GROUP(layer));
1422 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1423 }
1425 /// Callback
1426 static void
1427 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1428 SPDesktop *desktop)
1429 {
1430 desktop->_layer_changed_signal.emit (bottom);
1431 }
1433 /// Called when document is starting to be rebuilt.
1434 static void
1435 _reconstruction_start (SPDesktop * desktop)
1436 {
1437 // printf("Desktop, starting reconstruction\n");
1438 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1439 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1441 /*
1442 GSList const * selection_objs = desktop->selection->list();
1443 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1445 }
1446 */
1447 desktop->selection->clear();
1449 // printf("Desktop, starting reconstruction end\n");
1450 }
1452 /// Called when document rebuild is finished.
1453 static void
1454 _reconstruction_finish (SPDesktop * desktop)
1455 {
1456 // printf("Desktop, finishing reconstruction\n");
1457 if (desktop->_reconstruction_old_layer_id == NULL)
1458 return;
1460 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1461 if (newLayer != NULL)
1462 desktop->setCurrentLayer(newLayer);
1464 g_free(desktop->_reconstruction_old_layer_id);
1465 desktop->_reconstruction_old_layer_id = NULL;
1466 // printf("Desktop, finishing reconstruction end\n");
1467 return;
1468 }
1470 /**
1471 * Namedview_modified callback.
1472 */
1473 static void
1474 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1475 {
1476 SPNamedView *nv=SP_NAMEDVIEW(obj);
1478 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1480 /* Recalculate snap distances */
1481 /* FIXME: why is the desktop getting involved in setting up something
1482 ** that is entirely to do with the namedview?
1483 */
1484 _update_snap_distances (desktop);
1486 /* Show/hide page background */
1487 if (nv->pagecolor & 0xff) {
1488 sp_canvas_item_show (desktop->table);
1489 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1490 sp_canvas_item_move_to_z (desktop->table, 0);
1491 } else {
1492 sp_canvas_item_hide (desktop->table);
1493 }
1495 /* Show/hide page border */
1496 if (nv->showborder) {
1497 // show
1498 sp_canvas_item_show (desktop->page_border);
1499 // set color and shadow
1500 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1501 if (nv->pageshadow) {
1502 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1503 }
1504 // place in the z-order stack
1505 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1506 sp_canvas_item_move_to_z (desktop->page_border, 2);
1507 } else {
1508 int order = sp_canvas_item_order (desktop->page_border);
1509 int morder = sp_canvas_item_order (desktop->drawing);
1510 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1511 morder - order);
1512 }
1513 } else {
1514 sp_canvas_item_hide (desktop->page_border);
1515 if (nv->pageshadow) {
1516 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1517 }
1518 }
1520 /* Show/hide page shadow */
1521 if (nv->showpageshadow && nv->pageshadow) {
1522 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1523 } else {
1524 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1525 }
1527 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1528 (SP_RGBA32_R_U(nv->pagecolor) +
1529 SP_RGBA32_G_U(nv->pagecolor) +
1530 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1531 // the background color is light or transparent, use black outline
1532 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1533 } else { // use white outline
1534 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1535 }
1536 }
1537 }
1539 /**
1540 * Callback to reset snapper's distances.
1541 */
1542 static void
1543 _update_snap_distances (SPDesktop *desktop)
1544 {
1545 SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1547 SPNamedView &nv = *desktop->namedview;
1549 //tell all grid snappers
1550 for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1551 Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1552 grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
1553 *nv.gridtoleranceunit,
1554 px));
1555 }
1557 nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
1558 *nv.guidetoleranceunit,
1559 px));
1560 nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
1561 *nv.objecttoleranceunit,
1562 px));
1563 }
1566 NR::Matrix SPDesktop::w2d() const
1567 {
1568 return _w2d;
1569 }
1571 NR::Point SPDesktop::w2d(NR::Point const &p) const
1572 {
1573 return p * _w2d;
1574 }
1576 NR::Point SPDesktop::d2w(NR::Point const &p) const
1577 {
1578 return p * _d2w;
1579 }
1581 NR::Matrix SPDesktop::doc2dt() const
1582 {
1583 return _doc2dt;
1584 }
1586 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1587 {
1588 return p * _doc2dt;
1589 }
1591 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1592 {
1593 return p / _doc2dt;
1594 }
1597 /**
1598 * Pop event context from desktop's context stack. Never used.
1599 */
1600 // void
1601 // SPDesktop::pop_event_context (unsigned int key)
1602 // {
1603 // SPEventContext *ec = NULL;
1604 //
1605 // if (event_context && event_context->key == key) {
1606 // g_return_if_fail (event_context);
1607 // g_return_if_fail (event_context->next);
1608 // ec = event_context;
1609 // sp_event_context_deactivate (ec);
1610 // event_context = ec->next;
1611 // sp_event_context_activate (event_context);
1612 // _event_context_changed_signal.emit (this, ec);
1613 // }
1614 //
1615 // SPEventContext *ref = event_context;
1616 // while (ref && ref->next && ref->next->key != key)
1617 // ref = ref->next;
1618 //
1619 // if (ref && ref->next) {
1620 // ec = ref->next;
1621 // ref->next = ec->next;
1622 // }
1623 //
1624 // if (ec) {
1625 // sp_event_context_finish (ec);
1626 // g_object_unref (G_OBJECT (ec));
1627 // }
1628 // }
1630 /*
1631 Local Variables:
1632 mode:c++
1633 c-file-style:"stroustrup"
1634 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1635 indent-tabs-mode:nil
1636 fill-column:99
1637 End:
1638 */
1639 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :