1 #define __SP_DESKTOP_C__
3 /** \file
4 * Editable view implementation
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * MenTaLguY <mental@rydia.net>
9 * bulia byak <buliabyak@users.sf.net>
10 * Ralf Stephan <ralf@ark.in-berlin.de>
11 * John Bintz <jcoswell@coswellproductions.org>
12 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
13 *
14 * Copyright (C) 2007 Jon A. Cruz
15 * Copyright (C) 2006-2008 Johan Engelen
16 * Copyright (C) 2006 John Bintz
17 * Copyright (C) 2004 MenTaLguY
18 * Copyright (C) 1999-2002 Lauris Kaplinski
19 * Copyright (C) 2000-2001 Ximian, Inc.
20 *
21 * Released under GNU GPL, read the file 'COPYING' for more information
22 */
24 /** \class SPDesktop
25 * SPDesktop is a subclass of View, implementing an editable document
26 * canvas. It is extensively used by many UI controls that need certain
27 * visual representations of their own.
28 *
29 * SPDesktop provides a certain set of SPCanvasItems, serving as GUI
30 * layers of different control objects. The one containing the whole
31 * document is the drawing layer. In addition to it, there are grid,
32 * guide, sketch and control layers. The sketch layer is used for
33 * temporary drawing objects, before the real objects in document are
34 * created. The control layer contains editing knots, rubberband and
35 * similar non-document UI objects.
36 *
37 * Each SPDesktop is associated with a SPNamedView node of the document
38 * tree. Currently, all desktops are created from a single main named
39 * view, but in the future there may be support for different ones.
40 * SPNamedView serves as an in-document container for desktop-related
41 * data, like grid and guideline placement, snapping options and so on.
42 *
43 * Associated with each SPDesktop are the two most important editing
44 * related objects - SPSelection and SPEventContext.
45 *
46 * Sodipodi keeps track of the active desktop and invokes notification
47 * signals whenever it changes. UI elements can use these to update their
48 * display to the selection of the currently active editing window.
49 * (Lauris Kaplinski)
50 */
52 #ifdef HAVE_CONFIG_H
53 # include "config.h"
54 #endif
56 #include <glibmm/i18n.h>
57 #include <sigc++/functors/mem_fun.h>
58 #include <gtkmm.h>
60 #include <2geom/rect.h>
61 #include "macros.h"
62 #include "inkscape-private.h"
63 #include "desktop.h"
64 #include "desktop-events.h"
65 #include "desktop-handles.h"
66 #include "document.h"
67 #include "message-stack.h"
68 #include "selection.h"
69 #include "select-context.h"
70 #include "sp-namedview.h"
71 #include "color.h"
72 #include "sp-item-group.h"
73 #include "prefs-utils.h"
74 #include "object-hierarchy.h"
75 #include "helper/units.h"
76 #include "display/canvas-arena.h"
77 #include "display/nr-arena.h"
78 #include "display/gnome-canvas-acetate.h"
79 #include "display/sodipodi-ctrlrect.h"
80 #include "display/sp-canvas-util.h"
81 #include "display/canvas-temporary-item-list.h"
82 #include "display/snap-indicator.h"
83 #include "ui/dialog/dialog-manager.h"
84 #include "xml/repr.h"
85 #include "message-context.h"
86 #include "device-manager.h"
87 #include "layer-fns.h"
88 #include "layer-manager.h"
89 #include "event-log.h"
90 #include "display/canvas-grid.h"
91 #include "widgets/desktop-widget.h"
92 #include "box3d-context.h"
94 #include "display/sp-canvas.h"
96 namespace Inkscape { namespace XML { class Node; }}
98 // Callback declarations
99 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
100 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
101 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
102 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
103 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
104 static void _reconstruction_start(SPDesktop * desktop);
105 static void _reconstruction_finish(SPDesktop * desktop);
106 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
107 static void _update_snap_distances (SPDesktop *desktop);
109 /**
110 * Return new desktop object.
111 * \pre namedview != NULL.
112 * \pre canvas != NULL.
113 */
114 SPDesktop::SPDesktop() :
115 _dlg_mgr( 0 ),
116 namedview( 0 ),
117 canvas( 0 ),
118 selection( 0 ),
119 event_context( 0 ),
120 layer_manager( 0 ),
121 event_log( 0 ),
122 temporary_item_list( 0 ),
123 snapindicator( 0 ),
124 acetate( 0 ),
125 main( 0 ),
126 gridgroup( 0 ),
127 guides( 0 ),
128 drawing( 0 ),
129 sketch( 0 ),
130 controls( 0 ),
131 tempgroup ( 0 ),
132 table( 0 ),
133 page( 0 ),
134 page_border( 0 ),
135 current( 0 ),
136 zooms_past( 0 ),
137 zooms_future( 0 ),
138 dkey( 0 ),
139 number( 0 ),
140 window_state(0),
141 interaction_disabled_counter( 0 ),
142 waiting_cursor( false ),
143 guides_active( false ),
144 gr_item( 0 ),
145 gr_point_type( 0 ),
146 gr_point_i( 0 ),
147 gr_fill_or_stroke( true ),
148 _layer_hierarchy( 0 ),
149 _reconstruction_old_layer_id( 0 ),
150 _display_mode(Inkscape::RENDERMODE_NORMAL),
151 _saved_display_mode(Inkscape::RENDERMODE_NORMAL),
152 _widget( 0 ),
153 _inkscape( 0 ),
154 _guides_message_context( 0 ),
155 _active( false ),
156 _w2d(),
157 _d2w(),
158 _doc2dt( NR::Matrix(NR::scale(1, -1)) ),
159 grids_visible( false )
160 {
161 _d2w.setIdentity();
162 _w2d.setIdentity();
164 selection = Inkscape::GC::release( new Inkscape::Selection(this) );
165 }
167 void
168 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
169 {
170 // Temporary workaround for link order issues:
171 Inkscape::DeviceManager::getManager().getDevices();
173 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
175 current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
177 namedview = nv;
178 canvas = aCanvas;
180 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
181 /* Kill flicker */
182 sp_document_ensure_up_to_date (document);
184 /* Setup Dialog Manager */
185 _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
187 dkey = sp_item_display_key_new (1);
189 /* Connect document */
190 setDocument (document);
192 number = namedview->getViewCount();
195 /* Setup Canvas */
196 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
198 SPCanvasGroup *root = sp_canvas_root (canvas);
200 /* Setup adminstrative layers */
201 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
202 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
203 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
204 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
206 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
207 SP_CTRLRECT(table)->setRectangle(Geom::Rect(Geom::Point(-80000, -80000), Geom::Point(80000, 80000)));
208 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
209 sp_canvas_item_move_to_z (table, 0);
211 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
212 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
213 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
215 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
216 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
218 SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
220 if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
221 // Start in outline mode
222 setDisplayModeOutline();
223 } else {
224 // Start in normal mode, default
225 setDisplayModeNormal();
226 }
228 gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
229 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
230 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
231 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
232 tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
234 /* Push select tool to the bottom of stack */
235 /** \todo
236 * FIXME: this is the only call to this. Everything else seems to just
237 * call "set" instead of "push". Can we assume that there is only one
238 * context ever?
239 */
240 push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
242 // display rect and zoom are now handled in sp_desktop_widget_realize()
244 Geom::Rect const d(Geom::Point(0.0, 0.0),
245 Geom::Point(sp_document_width(document), sp_document_height(document)));
247 SP_CTRLRECT(page)->setRectangle(d);
248 SP_CTRLRECT(page_border)->setRectangle(d);
250 /* the following sets the page shadow on the canvas
251 It was originally set to 5, which is really cheesy!
252 It now is an attribute in the document's namedview. If a value of
253 0 is used, then the constructor for a shadow is not initialized.
254 */
256 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
257 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
258 }
261 /* Connect event for page resize */
262 _doc2dt[5] = sp_document_height (document);
263 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
265 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
267 NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
268 SP_CANVAS_ARENA (drawing)->arena,
269 dkey,
270 SP_ITEM_SHOW_DISPLAY);
271 if (ai) {
272 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
273 }
275 namedview->show(this);
276 /* Ugly hack */
277 activate_guides (true);
278 /* Ugly hack */
279 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
281 /* Set up notification of rebuilding the document, this allows
282 for saving object related settings in the document. */
283 _reconstruction_start_connection =
284 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
285 _reconstruction_finish_connection =
286 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
287 _reconstruction_old_layer_id = NULL;
289 // ?
290 // sp_active_desktop_set (desktop);
291 _inkscape = INKSCAPE;
293 _activate_connection = _activate_signal.connect(
294 sigc::bind(
295 sigc::ptr_fun(_onActivate),
296 this
297 )
298 );
299 _deactivate_connection = _deactivate_signal.connect(
300 sigc::bind(
301 sigc::ptr_fun(_onDeactivate),
302 this
303 )
304 );
306 _sel_modified_connection = selection->connectModified(
307 sigc::bind(
308 sigc::ptr_fun(&_onSelectionModified),
309 this
310 )
311 );
312 _sel_changed_connection = selection->connectChanged(
313 sigc::bind(
314 sigc::ptr_fun(&_onSelectionChanged),
315 this
316 )
317 );
320 /* setup LayerManager */
321 // (Setting up after the connections are all in place, as it may use some of them)
322 layer_manager = new Inkscape::LayerManager( this );
324 showGrids(namedview->grids_visible, false);
326 temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
327 snapindicator = new Inkscape::Display::SnapIndicator ( this );
328 }
331 void SPDesktop::destroy()
332 {
333 if (snapindicator) {
334 delete snapindicator;
335 snapindicator = NULL;
336 }
337 if (temporary_item_list) {
338 delete temporary_item_list;
339 temporary_item_list = NULL;
340 }
342 if (selection) {
343 delete selection;
344 selection = NULL;
345 }
347 namedview->hide(this);
349 _activate_connection.disconnect();
350 _deactivate_connection.disconnect();
351 _sel_modified_connection.disconnect();
352 _sel_changed_connection.disconnect();
353 _modified_connection.disconnect();
354 _commit_connection.disconnect();
355 _reconstruction_start_connection.disconnect();
356 _reconstruction_finish_connection.disconnect();
358 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
359 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
360 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
362 while (event_context) {
363 SPEventContext *ec = event_context;
364 event_context = ec->next;
365 sp_event_context_finish (ec);
366 g_object_unref (G_OBJECT (ec));
367 }
369 if (_layer_hierarchy) {
370 delete _layer_hierarchy;
371 // _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
372 }
374 if (layer_manager) {
375 delete layer_manager;
376 layer_manager = NULL;
377 }
379 if (_inkscape) {
380 _inkscape = NULL;
381 }
383 if (drawing) {
384 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
385 drawing = NULL;
386 }
388 delete _guides_message_context;
389 _guides_message_context = NULL;
391 g_list_free (zooms_past);
392 g_list_free (zooms_future);
393 }
395 SPDesktop::~SPDesktop() {}
397 //--------------------------------------------------------------------
398 /* Public methods */
401 /* These methods help for temporarily showing things on-canvas.
402 * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
403 * is when you want to prematurely remove the item from the canvas, by calling
404 * desktop->remove_temporary_canvasitem(tempitem).
405 */
406 /** Note that lifetime is measured in milliseconds
407 * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
408 * delete the object for you and the reference will become invalid without you knowing it.
409 * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
410 * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
411 * because the object might be deleted already without you knowing it.
412 * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
413 */
414 Inkscape::Display::TemporaryItem *
415 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
416 {
417 if (move_to_bottom) {
418 sp_canvas_item_move_to_z(item, 0);
419 }
421 return temporary_item_list->add_item(item, lifetime);
422 }
424 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
425 */
426 void
427 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
428 {
429 // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
430 if (tempitem && temporary_item_list) {
431 temporary_item_list->delete_item(tempitem);
432 }
433 }
435 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
436 SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
437 canvas->rendermode = mode;
438 _display_mode = mode;
439 if (mode != Inkscape::RENDERMODE_OUTLINE) {
440 _saved_display_mode = _display_mode;
441 }
442 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
443 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
444 }
446 void SPDesktop::displayModeToggle() {
447 if (_display_mode == Inkscape::RENDERMODE_OUTLINE) {
448 _setDisplayMode(_saved_display_mode);
449 } else {
450 _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
451 }
452 }
454 /**
455 * Returns current root (=bottom) layer.
456 */
457 SPObject *SPDesktop::currentRoot() const
458 {
459 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
460 }
462 /**
463 * Returns current top layer.
464 */
465 SPObject *SPDesktop::currentLayer() const
466 {
467 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
468 }
470 /**
471 * Sets the current layer of the desktop.
472 *
473 * Make \a object the top layer.
474 */
475 void SPDesktop::setCurrentLayer(SPObject *object) {
476 g_return_if_fail(SP_IS_GROUP(object));
477 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
478 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
479 _layer_hierarchy->setBottom(object);
480 }
482 void SPDesktop::toggleLayerSolo(SPObject *object) {
483 g_return_if_fail(SP_IS_GROUP(object));
484 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
486 bool othersShowing = false;
487 std::vector<SPObject*> layers;
488 for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
489 layers.push_back(obj);
490 othersShowing |= !SP_ITEM(obj)->isHidden();
491 }
492 for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
493 layers.push_back(obj);
494 othersShowing |= !SP_ITEM(obj)->isHidden();
495 }
498 if ( SP_ITEM(object)->isHidden() ) {
499 SP_ITEM(object)->setHidden(false);
500 }
502 for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
503 SP_ITEM(*it)->setHidden(othersShowing);
504 }
505 }
507 /**
508 * Return layer that contains \a object.
509 */
510 SPObject *SPDesktop::layerForObject(SPObject *object) {
511 g_return_val_if_fail(object != NULL, NULL);
513 SPObject *root=currentRoot();
514 object = SP_OBJECT_PARENT(object);
515 while ( object && object != root && !isLayer(object) ) {
516 object = SP_OBJECT_PARENT(object);
517 }
518 return object;
519 }
521 /**
522 * True if object is a layer.
523 */
524 bool SPDesktop::isLayer(SPObject *object) const {
525 return ( SP_IS_GROUP(object)
526 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
527 == SPGroup::LAYER ) );
528 }
530 /**
531 * True if desktop viewport fully contains \a item's bbox.
532 */
533 bool SPDesktop::isWithinViewport (SPItem *item) const
534 {
535 Geom::Rect const viewport = get_display_area();
536 boost::optional<NR::Rect> const bbox = sp_item_bbox_desktop(item);
537 if (bbox) {
538 return viewport.contains(to_2geom(*bbox));
539 } else {
540 return true;
541 }
542 }
544 ///
545 bool SPDesktop::itemIsHidden(SPItem const *item) const {
546 return item->isHidden(this->dkey);
547 }
549 /**
550 * Set activate property of desktop; emit signal if changed.
551 */
552 void
553 SPDesktop::set_active (bool new_active)
554 {
555 if (new_active != _active) {
556 _active = new_active;
557 if (new_active) {
558 _activate_signal.emit();
559 } else {
560 _deactivate_signal.emit();
561 }
562 }
563 }
565 /**
566 * Set activate status of current desktop's named view.
567 */
568 void
569 SPDesktop::activate_guides(bool activate)
570 {
571 guides_active = activate;
572 namedview->activateGuides(this, activate);
573 }
575 /**
576 * Make desktop switch documents.
577 */
578 void
579 SPDesktop::change_document (SPDocument *theDocument)
580 {
581 g_return_if_fail (theDocument != NULL);
583 /* unselect everything before switching documents */
584 selection->clear();
586 setDocument (theDocument);
588 /* update the rulers, connect the desktop widget's signal to the new namedview etc.
589 (this can probably be done in a better way) */
590 Gtk::Window *parent = this->getToplevel();
591 g_assert(parent != NULL);
592 SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
593 if (dtw) dtw->desktop = this;
594 sp_desktop_widget_update_namedview(dtw);
596 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
597 _document_replaced_signal.emit (this, theDocument);
598 }
600 /**
601 * Make desktop switch event contexts.
602 */
603 void
604 SPDesktop::set_event_context (GtkType type, const gchar *config)
605 {
606 SPEventContext *ec;
607 while (event_context) {
608 ec = event_context;
609 sp_event_context_deactivate (ec);
610 event_context = ec->next;
611 sp_event_context_finish (ec);
612 g_object_unref (G_OBJECT (ec));
613 }
615 Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
616 ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
617 ec->next = event_context;
618 event_context = ec;
619 sp_event_context_activate (ec);
620 _event_context_changed_signal.emit (this, ec);
621 }
623 /**
624 * Push event context onto desktop's context stack.
625 */
626 void
627 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
628 {
629 SPEventContext *ref, *ec;
630 Inkscape::XML::Node *repr;
632 if (event_context && event_context->key == key) return;
633 ref = event_context;
634 while (ref && ref->next && ref->next->key != key) ref = ref->next;
635 if (ref && ref->next) {
636 ec = ref->next;
637 ref->next = ec->next;
638 sp_event_context_finish (ec);
639 g_object_unref (G_OBJECT (ec));
640 }
642 if (event_context) sp_event_context_deactivate (event_context);
643 repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
644 ec = sp_event_context_new (type, this, repr, key);
645 ec->next = event_context;
646 event_context = ec;
647 sp_event_context_activate (ec);
648 _event_context_changed_signal.emit (this, ec);
649 }
651 /**
652 * Sets the coordinate status to a given point
653 */
654 void
655 SPDesktop::set_coordinate_status (Geom::Point p) {
656 _widget->setCoordinateStatus(p);
657 }
659 /**
660 * \see sp_document_item_from_list_at_point_bottom()
661 */
662 SPItem *
663 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
664 {
665 g_return_val_if_fail (doc() != NULL, NULL);
666 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
667 }
669 /**
670 * \see sp_document_item_at_point()
671 */
672 SPItem *
673 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
674 {
675 g_return_val_if_fail (doc() != NULL, NULL);
676 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
677 }
679 /**
680 * \see sp_document_group_at_point()
681 */
682 SPItem *
683 SPDesktop::group_at_point (Geom::Point const p) const
684 {
685 g_return_val_if_fail (doc() != NULL, NULL);
686 return sp_document_group_at_point (doc(), dkey, p);
687 }
689 /**
690 * \brief Returns the mouse point in document coordinates; if mouse is
691 * outside the canvas, returns the center of canvas viewpoint
692 */
693 Geom::Point
694 SPDesktop::point() const
695 {
696 Geom::Point p = _widget->getPointer();
697 Geom::Point pw = sp_canvas_window_to_world (canvas, p);
698 p = w2d(pw);
700 Geom::Rect const r = canvas->getViewbox();
702 Geom::Point r0 = w2d(r.min());
703 Geom::Point r1 = w2d(r.max());
705 if (p[Geom::X] >= r0[Geom::X] &&
706 p[Geom::X] <= r1[Geom::X] &&
707 p[Geom::Y] >= r1[Geom::Y] &&
708 p[Geom::Y] <= r0[Geom::Y])
709 {
710 return p;
711 } else {
712 return (r0 + r1) / 2;
713 }
714 }
716 /**
717 * Put current zoom data in history list.
718 */
719 void
720 SPDesktop::push_current_zoom (GList **history)
721 {
722 Geom::Rect const area = get_display_area();
724 NRRect *old_zoom = g_new(NRRect, 1);
725 old_zoom->x0 = area.min()[NR::X];
726 old_zoom->x1 = area.max()[NR::X];
727 old_zoom->y0 = area.min()[NR::Y];
728 old_zoom->y1 = area.max()[NR::Y];
729 if ( *history == NULL
730 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
731 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
732 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
733 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
734 {
735 *history = g_list_prepend (*history, old_zoom);
736 }
737 }
739 /**
740 * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
741 */
742 void
743 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
744 {
745 g_assert(_widget);
747 // save the zoom
748 if (log) {
749 push_current_zoom(&zooms_past);
750 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
751 g_list_free (zooms_future);
752 zooms_future = NULL;
753 }
755 double const cx = 0.5 * (x0 + x1);
756 double const cy = 0.5 * (y0 + y1);
758 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
759 Geom::Rect viewbox = canvas->getViewbox();
760 viewbox.expandBy(border);
762 double scale = _d2w.descrim();
763 double newscale;
764 if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
765 newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
766 } else {
767 newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
768 }
770 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
772 int clear = FALSE;
773 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
774 /* Set zoom factors */
775 _d2w = NR::Matrix(NR::scale(newscale, -newscale));
776 _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
777 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
778 clear = TRUE;
779 }
781 /* Calculate top left corner (in document pixels) */
782 x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
783 y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
785 /* Scroll */
786 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
788 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
789 sp_box3d_context_update_lines(event_context);
791 _widget->updateRulers();
792 _widget->updateScrollbars(_d2w.descrim());
793 _widget->updateZoom();
794 }
796 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
797 {
798 set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
799 }
801 /**
802 * Return viewbox dimensions.
803 */
804 Geom::Rect SPDesktop::get_display_area() const
805 {
806 Geom::Rect const viewbox = canvas->getViewbox();
808 double const scale = _d2w[0];
810 return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
811 Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
812 }
814 /**
815 * Revert back to previous zoom if possible.
816 */
817 void
818 SPDesktop::prev_zoom()
819 {
820 if (zooms_past == NULL) {
821 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
822 return;
823 }
825 // push current zoom into forward zooms list
826 push_current_zoom (&zooms_future);
828 // restore previous zoom
829 set_display_area (((NRRect *) zooms_past->data)->x0,
830 ((NRRect *) zooms_past->data)->y0,
831 ((NRRect *) zooms_past->data)->x1,
832 ((NRRect *) zooms_past->data)->y1,
833 0, false);
835 // remove the just-added zoom from the past zooms list
836 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
837 }
839 /**
840 * Set zoom to next in list.
841 */
842 void
843 SPDesktop::next_zoom()
844 {
845 if (zooms_future == NULL) {
846 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
847 return;
848 }
850 // push current zoom into past zooms list
851 push_current_zoom (&zooms_past);
853 // restore next zoom
854 set_display_area (((NRRect *) zooms_future->data)->x0,
855 ((NRRect *) zooms_future->data)->y0,
856 ((NRRect *) zooms_future->data)->x1,
857 ((NRRect *) zooms_future->data)->y1,
858 0, false);
860 // remove the just-used zoom from the zooms_future list
861 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
862 }
864 /**
865 * Zoom to point with absolute zoom factor.
866 */
867 void
868 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
869 {
870 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
872 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
873 // this check prevents "sliding" when trying to zoom in at maximum zoom;
874 /// \todo someone please fix calculations properly and remove this hack
875 if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
876 return;
878 Geom::Rect const viewbox = canvas->getViewbox();
880 double const width2 = viewbox.dimensions()[NR::X] / zoom;
881 double const height2 = viewbox.dimensions()[NR::Y] / zoom;
883 set_display_area(cx - px * width2,
884 cy - py * height2,
885 cx + (1 - px) * width2,
886 cy + (1 - py) * height2,
887 0.0);
888 }
890 /**
891 * Zoom to center with absolute zoom factor.
892 */
893 void
894 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
895 {
896 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
897 }
899 /**
900 * Zoom to point with relative zoom factor.
901 */
902 void
903 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
904 {
905 Geom::Rect const area = get_display_area();
907 if (cx < area.min()[NR::X]) {
908 cx = area.min()[NR::X];
909 }
910 if (cx > area.max()[NR::X]) {
911 cx = area.max()[NR::X];
912 }
913 if (cy < area.min()[NR::Y]) {
914 cy = area.min()[NR::Y];
915 }
916 if (cy > area.max()[NR::Y]) {
917 cy = area.max()[NR::Y];
918 }
920 gdouble const scale = _d2w.descrim() * zoom;
921 double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
922 double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
924 zoom_absolute_keep_point(cx, cy, px, py, scale);
925 }
927 /**
928 * Zoom to center with relative zoom factor.
929 */
930 void
931 SPDesktop::zoom_relative (double cx, double cy, double zoom)
932 {
933 gdouble scale = _d2w.descrim() * zoom;
934 zoom_absolute (cx, cy, scale);
935 }
937 /**
938 * Set display area to origin and current document dimensions.
939 */
940 void
941 SPDesktop::zoom_page()
942 {
943 Geom::Rect d(Geom::Point(0, 0),
944 Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
946 // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 1.0; is it safe to ignore it?
947 if (d.isEmpty()) {
948 return;
949 }
951 set_display_area(d, 10);
952 }
954 /**
955 * Set display area to current document width.
956 */
957 void
958 SPDesktop::zoom_page_width()
959 {
960 Geom::Rect const a = get_display_area();
962 if (sp_document_width(doc()) < 1.0) {
963 return;
964 }
966 Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
967 Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
969 set_display_area(d, 10);
970 }
972 /**
973 * Zoom to selection.
974 */
975 void
976 SPDesktop::zoom_selection()
977 {
978 boost::optional<Geom::Rect> const d = to_2geom(selection->bounds());
980 // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 0.1; is it safe to ignore it?
981 if ( !d || d->isEmpty() ) {
982 return;
983 }
985 set_display_area(*d, 10);
986 }
988 /**
989 * Tell widget to let zoom widget grab keyboard focus.
990 */
991 void
992 SPDesktop::zoom_grab_focus()
993 {
994 _widget->letZoomGrabFocus();
995 }
997 /**
998 * Zoom to whole drawing.
999 */
1000 void
1001 SPDesktop::zoom_drawing()
1002 {
1003 g_return_if_fail (doc() != NULL);
1004 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1005 g_return_if_fail (docitem != NULL);
1007 boost::optional<Geom::Rect> d = to_2geom(sp_item_bbox_desktop(docitem));
1009 /* Note that the second condition here indicates that
1010 ** there are no items in the drawing.
1011 */
1012 // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 1.0; is it safe to ignore it?
1013 if ( !d || d->isEmpty() ) {
1014 return;
1015 }
1017 set_display_area(*d, 10);
1018 }
1020 /**
1021 * Scroll canvas by specific coordinate amount in svg coordinates.
1022 */
1023 void
1024 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1025 {
1026 double scale = _d2w.descrim();
1027 scroll_world(dx*scale, dy*scale, is_scrolling);
1028 }
1030 /**
1031 * Scroll canvas by specific coordinate amount.
1032 */
1033 void
1034 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1035 {
1036 g_assert(_widget);
1038 Geom::Rect const viewbox = canvas->getViewbox();
1040 sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1042 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1043 sp_box3d_context_update_lines(event_context);
1045 _widget->updateRulers();
1046 _widget->updateScrollbars(_d2w.descrim());
1047 }
1049 bool
1050 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1051 {
1052 gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
1054 // autoscrolldistance is in screen pixels, but the display area is in document units
1055 autoscrolldistance /= _d2w.descrim();
1056 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1057 Geom::Rect dbox = get_display_area();
1058 dbox.expandBy(-autoscrolldistance);
1060 if (!(p[NR::X] > dbox.min()[NR::X] && p[NR::X] < dbox.max()[NR::X]) ||
1061 !(p[NR::Y] > dbox.min()[NR::Y] && p[NR::Y] < dbox.max()[NR::Y]) ) {
1063 Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1065 gdouble x_to;
1066 if (p[NR::X] < dbox.min()[NR::X])
1067 x_to = dbox.min()[NR::X];
1068 else if (p[NR::X] > dbox.max()[NR::X])
1069 x_to = dbox.max()[NR::X];
1070 else
1071 x_to = p[NR::X];
1073 gdouble y_to;
1074 if (p[NR::Y] < dbox.min()[NR::Y])
1075 y_to = dbox.min()[NR::Y];
1076 else if (p[NR::Y] > dbox.max()[NR::Y])
1077 y_to = dbox.max()[NR::Y];
1078 else
1079 y_to = p[NR::Y];
1081 Geom::Point const d_dt(x_to, y_to);
1082 Geom::Point const d_w( d_dt * _d2w );
1083 Geom::Point const moved_w( d_w - s_w );
1085 if (autoscrollspeed == 0)
1086 autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
1088 if (autoscrollspeed != 0)
1089 scroll_world (autoscrollspeed * moved_w);
1091 return true;
1092 }
1093 return false;
1094 }
1096 bool
1097 SPDesktop::is_iconified()
1098 {
1099 return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1100 }
1102 void
1103 SPDesktop::iconify()
1104 {
1105 _widget->setIconified();
1106 }
1108 bool
1109 SPDesktop::is_maximized()
1110 {
1111 return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1112 }
1114 void
1115 SPDesktop::maximize()
1116 {
1117 _widget->setMaximized();
1118 }
1120 bool
1121 SPDesktop::is_fullscreen()
1122 {
1123 return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1124 }
1126 void
1127 SPDesktop::fullscreen()
1128 {
1129 _widget->setFullscreen();
1130 }
1132 void
1133 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1134 {
1135 _widget->getGeometry (x, y, w, h);
1136 }
1138 void
1139 SPDesktop::setWindowPosition (Geom::Point p)
1140 {
1141 _widget->setPosition (p);
1142 }
1144 void
1145 SPDesktop::setWindowSize (gint w, gint h)
1146 {
1147 _widget->setSize (w, h);
1148 }
1150 void
1151 SPDesktop::setWindowTransient (void *p, int transient_policy)
1152 {
1153 _widget->setTransient (p, transient_policy);
1154 }
1156 Gtk::Window*
1157 SPDesktop::getToplevel( )
1158 {
1159 return _widget->getWindow();
1160 }
1162 void
1163 SPDesktop::presentWindow()
1164 {
1165 _widget->present();
1166 }
1168 bool
1169 SPDesktop::warnDialog (gchar *text)
1170 {
1171 return _widget->warnDialog (text);
1172 }
1174 void
1175 SPDesktop::toggleRulers()
1176 {
1177 _widget->toggleRulers();
1178 }
1180 void
1181 SPDesktop::toggleScrollbars()
1182 {
1183 _widget->toggleScrollbars();
1184 }
1186 void
1187 SPDesktop::layoutWidget()
1188 {
1189 _widget->layout();
1190 }
1192 void
1193 SPDesktop::destroyWidget()
1194 {
1195 _widget->destroy();
1196 }
1198 bool
1199 SPDesktop::shutdown()
1200 {
1201 return _widget->shutdown();
1202 }
1204 bool SPDesktop::onDeleteUI (GdkEventAny*)
1205 {
1206 if(shutdown())
1207 return true;
1209 destroyWidget();
1210 return false;
1211 }
1213 /**
1214 * onWindowStateEvent
1215 *
1216 * Called when the window changes its maximize/fullscreen/iconify/pinned state.
1217 * Since GTK doesn't have a way to query this state information directly, we
1218 * record it for the desktop here, and also possibly trigger a layout.
1219 */
1220 bool
1221 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1222 {
1223 // Record the desktop window's state
1224 window_state = event->new_window_state;
1226 // Layout may differ depending on full-screen mode or not
1227 GdkWindowState changed = event->changed_mask;
1228 if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1229 layoutWidget();
1230 }
1232 return false;
1233 }
1235 void
1236 SPDesktop::setToolboxFocusTo (gchar const *label)
1237 {
1238 _widget->setToolboxFocusTo (label);
1239 }
1241 void
1242 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1243 {
1244 _widget->setToolboxAdjustmentValue (id, val);
1245 }
1247 void
1248 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1249 {
1250 _widget->setToolboxSelectOneValue (id, val);
1251 }
1253 bool
1254 SPDesktop::isToolboxButtonActive (gchar const *id)
1255 {
1256 return _widget->isToolboxButtonActive (id);
1257 }
1259 void
1260 SPDesktop::emitToolSubselectionChanged(gpointer data)
1261 {
1262 _tool_subselection_changed.emit(data);
1263 inkscape_subselection_changed (this);
1264 }
1266 void
1267 SPDesktop::updateNow()
1268 {
1269 sp_canvas_update_now(canvas);
1270 }
1272 void
1273 SPDesktop::enableInteraction()
1274 {
1275 _widget->enableInteraction();
1276 }
1278 void SPDesktop::disableInteraction()
1279 {
1280 _widget->disableInteraction();
1281 }
1283 void SPDesktop::setWaitingCursor()
1284 {
1285 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1286 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1287 gdk_cursor_unref(waiting);
1288 // GDK needs the flush for the cursor change to take effect
1289 gdk_flush();
1290 waiting_cursor = true;
1291 }
1293 void SPDesktop::clearWaitingCursor()
1294 {
1295 if (waiting_cursor)
1296 sp_event_context_update_cursor(sp_desktop_event_context(this));
1297 }
1299 void SPDesktop::toggleColorProfAdjust()
1300 {
1301 _widget->toggleColorProfAdjust();
1302 }
1304 void SPDesktop::toggleGrids()
1305 {
1306 if (namedview->grids) {
1307 if(gridgroup) {
1308 showGrids(!grids_visible);
1309 }
1310 } else {
1311 //there is no grid present at the moment. add a rectangular grid and make it visible
1312 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1313 Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1314 showGrids(true);
1315 }
1316 }
1318 void SPDesktop::showGrids(bool show, bool dirty_document)
1319 {
1320 grids_visible = show;
1321 sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1322 if (show) {
1323 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1324 } else {
1325 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1326 }
1327 }
1329 void SPDesktop::toggleSnapping()
1330 {
1331 bool v = namedview->snap_manager.getSnapEnabledGlobally();
1332 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1333 sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1334 }
1336 //----------------------------------------------------------------------
1337 // Callback implementations. The virtual ones are connected by the view.
1339 void
1340 SPDesktop::onPositionSet (double x, double y)
1341 {
1342 _widget->viewSetPosition (Geom::Point(x,y));
1343 }
1345 void
1346 SPDesktop::onResized (double /*x*/, double /*y*/)
1347 {
1348 // Nothing called here
1349 }
1351 /**
1352 * Redraw callback; queues Gtk redraw; connected by View.
1353 */
1354 void
1355 SPDesktop::onRedrawRequested ()
1356 {
1357 if (main) {
1358 _widget->requestCanvasUpdate();
1359 }
1360 }
1362 void
1363 SPDesktop::updateCanvasNow()
1364 {
1365 _widget->requestCanvasUpdateAndWait();
1366 }
1368 /**
1369 * Associate document with desktop.
1370 */
1371 void
1372 SPDesktop::setDocument (SPDocument *doc)
1373 {
1374 if (this->doc() && doc) {
1375 namedview->hide(this);
1376 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1377 }
1379 if (_layer_hierarchy) {
1380 _layer_hierarchy->clear();
1381 delete _layer_hierarchy;
1382 }
1383 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1384 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1385 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1386 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1387 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1389 /* setup EventLog */
1390 event_log = new Inkscape::EventLog(doc);
1391 doc->addUndoObserver(*event_log);
1393 _commit_connection.disconnect();
1394 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1396 /// \todo fixme: This condition exists to make sure the code
1397 /// inside is NOT called on initialization, only on replacement. But there
1398 /// are surely more safe methods to accomplish this.
1399 // TODO since the comment had reversed logic, check the intent of this block of code:
1400 if (drawing) {
1401 NRArenaItem *ai = 0;
1403 namedview = sp_document_namedview (doc, NULL);
1404 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1405 number = namedview->getViewCount();
1407 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1408 SP_CANVAS_ARENA (drawing)->arena,
1409 dkey,
1410 SP_ITEM_SHOW_DISPLAY);
1411 if (ai) {
1412 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1413 }
1414 namedview->show(this);
1415 /* Ugly hack */
1416 activate_guides (true);
1417 /* Ugly hack */
1418 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1419 }
1421 _document_replaced_signal.emit (this, doc);
1423 View::setDocument (doc);
1424 }
1426 void
1427 SPDesktop::onStatusMessage
1428 (Inkscape::MessageType type, gchar const *message)
1429 {
1430 if (_widget) {
1431 _widget->setMessage(type, message);
1432 }
1433 }
1435 void
1436 SPDesktop::onDocumentURISet (gchar const* uri)
1437 {
1438 _widget->setTitle(uri);
1439 }
1441 /**
1442 * Resized callback.
1443 */
1444 void
1445 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1446 {
1447 _doc2dt[5] = height;
1448 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1449 Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1450 SP_CTRLRECT(page)->setRectangle(a);
1451 SP_CTRLRECT(page_border)->setRectangle(a);
1452 }
1455 void
1456 SPDesktop::_onActivate (SPDesktop* dt)
1457 {
1458 if (!dt->_widget) return;
1459 dt->_widget->activateDesktop();
1460 }
1462 void
1463 SPDesktop::_onDeactivate (SPDesktop* dt)
1464 {
1465 if (!dt->_widget) return;
1466 dt->_widget->deactivateDesktop();
1467 }
1469 void
1470 SPDesktop::_onSelectionModified
1471 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1472 {
1473 if (!dt->_widget) return;
1474 dt->_widget->updateScrollbars (dt->_d2w.descrim());
1475 }
1477 static void
1478 _onSelectionChanged
1479 (Inkscape::Selection *selection, SPDesktop *desktop)
1480 {
1481 /** \todo
1482 * only change the layer for single selections, or what?
1483 * This seems reasonable -- for multiple selections there can be many
1484 * different layers involved.
1485 */
1486 SPItem *item=selection->singleItem();
1487 if (item) {
1488 SPObject *layer=desktop->layerForObject(item);
1489 if ( layer && layer != desktop->currentLayer() ) {
1490 desktop->setCurrentLayer(layer);
1491 }
1492 }
1493 }
1495 /**
1496 * Calls event handler of current event context.
1497 * \param arena Unused
1498 * \todo fixme
1499 */
1500 static gint
1501 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1502 {
1503 if (ai) {
1504 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1505 return sp_event_context_item_handler (desktop->event_context, spi, event);
1506 } else {
1507 return sp_event_context_root_handler (desktop->event_context, event);
1508 }
1509 }
1511 static void
1512 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1513 g_return_if_fail(SP_IS_GROUP(layer));
1514 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1515 }
1517 /// Callback
1518 static void
1519 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1520 g_return_if_fail(SP_IS_GROUP(layer));
1521 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1522 }
1524 /// Callback
1525 static void
1526 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1527 SPDesktop *desktop)
1528 {
1529 desktop->_layer_changed_signal.emit (bottom);
1530 }
1532 /// Called when document is starting to be rebuilt.
1533 static void
1534 _reconstruction_start (SPDesktop * desktop)
1535 {
1536 // printf("Desktop, starting reconstruction\n");
1537 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1538 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1540 /*
1541 GSList const * selection_objs = desktop->selection->list();
1542 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1544 }
1545 */
1546 desktop->selection->clear();
1548 // printf("Desktop, starting reconstruction end\n");
1549 }
1551 /// Called when document rebuild is finished.
1552 static void
1553 _reconstruction_finish (SPDesktop * desktop)
1554 {
1555 // printf("Desktop, finishing reconstruction\n");
1556 if (desktop->_reconstruction_old_layer_id == NULL)
1557 return;
1559 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1560 if (newLayer != NULL)
1561 desktop->setCurrentLayer(newLayer);
1563 g_free(desktop->_reconstruction_old_layer_id);
1564 desktop->_reconstruction_old_layer_id = NULL;
1565 // printf("Desktop, finishing reconstruction end\n");
1566 return;
1567 }
1569 /**
1570 * Namedview_modified callback.
1571 */
1572 static void
1573 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1574 {
1575 SPNamedView *nv=SP_NAMEDVIEW(obj);
1577 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1579 /* Recalculate snap distances */
1580 /* FIXME: why is the desktop getting involved in setting up something
1581 ** that is entirely to do with the namedview?
1582 */
1583 _update_snap_distances (desktop);
1585 /* Show/hide page background */
1586 if (nv->pagecolor & 0xff) {
1587 sp_canvas_item_show (desktop->table);
1588 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1589 sp_canvas_item_move_to_z (desktop->table, 0);
1590 } else {
1591 sp_canvas_item_hide (desktop->table);
1592 }
1594 /* Show/hide page border */
1595 if (nv->showborder) {
1596 // show
1597 sp_canvas_item_show (desktop->page_border);
1598 // set color and shadow
1599 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1600 if (nv->pageshadow) {
1601 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1602 }
1603 // place in the z-order stack
1604 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1605 sp_canvas_item_move_to_z (desktop->page_border, 2);
1606 } else {
1607 int order = sp_canvas_item_order (desktop->page_border);
1608 int morder = sp_canvas_item_order (desktop->drawing);
1609 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1610 morder - order);
1611 }
1612 } else {
1613 sp_canvas_item_hide (desktop->page_border);
1614 if (nv->pageshadow) {
1615 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1616 }
1617 }
1619 /* Show/hide page shadow */
1620 if (nv->showpageshadow && nv->pageshadow) {
1621 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1622 } else {
1623 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1624 }
1626 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1627 (SP_RGBA32_R_U(nv->pagecolor) +
1628 SP_RGBA32_G_U(nv->pagecolor) +
1629 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1630 // the background color is light or transparent, use black outline
1631 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1632 } else { // use white outline
1633 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1634 }
1635 }
1636 }
1638 /**
1639 * Callback to reset snapper's distances.
1640 */
1641 static void
1642 _update_snap_distances (SPDesktop *desktop)
1643 {
1644 SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1646 SPNamedView &nv = *desktop->namedview;
1648 //tell all grid snappers
1649 for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1650 Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1651 grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
1652 *nv.gridtoleranceunit,
1653 px));
1654 }
1656 nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
1657 *nv.guidetoleranceunit,
1658 px));
1659 nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
1660 *nv.objecttoleranceunit,
1661 px));
1662 }
1665 Geom::Matrix SPDesktop::w2d() const
1666 {
1667 return _w2d;
1668 }
1670 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1671 {
1672 return p * _w2d;
1673 }
1675 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1676 {
1677 return p * _d2w;
1678 }
1680 Geom::Matrix SPDesktop::doc2dt() const
1681 {
1682 return _doc2dt;
1683 }
1685 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1686 {
1687 return p * _doc2dt;
1688 }
1690 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1691 {
1692 return p * _doc2dt.inverse();
1693 }
1696 /**
1697 * Pop event context from desktop's context stack. Never used.
1698 */
1699 // void
1700 // SPDesktop::pop_event_context (unsigned int key)
1701 // {
1702 // SPEventContext *ec = NULL;
1703 //
1704 // if (event_context && event_context->key == key) {
1705 // g_return_if_fail (event_context);
1706 // g_return_if_fail (event_context->next);
1707 // ec = event_context;
1708 // sp_event_context_deactivate (ec);
1709 // event_context = ec->next;
1710 // sp_event_context_activate (event_context);
1711 // _event_context_changed_signal.emit (this, ec);
1712 // }
1713 //
1714 // SPEventContext *ref = event_context;
1715 // while (ref && ref->next && ref->next->key != key)
1716 // ref = ref->next;
1717 //
1718 // if (ref && ref->next) {
1719 // ec = ref->next;
1720 // ref->next = ec->next;
1721 // }
1722 //
1723 // if (ec) {
1724 // sp_event_context_finish (ec);
1725 // g_object_unref (G_OBJECT (ec));
1726 // }
1727 // }
1729 /*
1730 Local Variables:
1731 mode:c++
1732 c-file-style:"stroustrup"
1733 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1734 indent-tabs-mode:nil
1735 fill-column:99
1736 End:
1737 */
1738 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :