92946cb7dfa5e04e6261452849753f674fb2e441
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 namedview->hide(this);
344 _activate_connection.disconnect();
345 _deactivate_connection.disconnect();
346 _sel_modified_connection.disconnect();
347 _sel_changed_connection.disconnect();
348 _modified_connection.disconnect();
349 _commit_connection.disconnect();
350 _reconstruction_start_connection.disconnect();
351 _reconstruction_finish_connection.disconnect();
353 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
354 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
355 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
357 while (event_context) {
358 SPEventContext *ec = event_context;
359 event_context = ec->next;
360 sp_event_context_finish (ec);
361 g_object_unref (G_OBJECT (ec));
362 }
364 if (_layer_hierarchy) {
365 delete _layer_hierarchy;
366 // _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
367 }
369 if (layer_manager) {
370 delete layer_manager;
371 layer_manager = NULL;
372 }
374 if (_inkscape) {
375 _inkscape = NULL;
376 }
378 if (drawing) {
379 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
380 drawing = NULL;
381 }
383 delete _guides_message_context;
384 _guides_message_context = NULL;
386 g_list_free (zooms_past);
387 g_list_free (zooms_future);
388 }
390 SPDesktop::~SPDesktop() {}
392 //--------------------------------------------------------------------
393 /* Public methods */
396 /* These methods help for temporarily showing things on-canvas.
397 * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
398 * is when you want to prematurely remove the item from the canvas, by calling
399 * desktop->remove_temporary_canvasitem(tempitem).
400 */
401 /** Note that lifetime is measured in milliseconds
402 * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
403 * delete the object for you and the reference will become invalid without you knowing it.
404 * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
405 * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
406 * because the object might be deleted already without you knowing it.
407 * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
408 */
409 Inkscape::Display::TemporaryItem *
410 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
411 {
412 if (move_to_bottom) {
413 sp_canvas_item_move_to_z(item, 0);
414 }
416 return temporary_item_list->add_item(item, lifetime);
417 }
419 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
420 */
421 void
422 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
423 {
424 // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
425 if (tempitem && temporary_item_list) {
426 temporary_item_list->delete_item(tempitem);
427 }
428 }
430 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
431 SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
432 canvas->rendermode = mode;
433 _display_mode = mode;
434 if (mode != Inkscape::RENDERMODE_OUTLINE) {
435 _saved_display_mode = _display_mode;
436 }
437 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
438 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
439 }
441 void SPDesktop::displayModeToggle() {
442 if (_display_mode == Inkscape::RENDERMODE_OUTLINE) {
443 _setDisplayMode(_saved_display_mode);
444 } else {
445 _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
446 }
447 }
449 /**
450 * Returns current root (=bottom) layer.
451 */
452 SPObject *SPDesktop::currentRoot() const
453 {
454 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
455 }
457 /**
458 * Returns current top layer.
459 */
460 SPObject *SPDesktop::currentLayer() const
461 {
462 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
463 }
465 /**
466 * Sets the current layer of the desktop.
467 *
468 * Make \a object the top layer.
469 */
470 void SPDesktop::setCurrentLayer(SPObject *object) {
471 g_return_if_fail(SP_IS_GROUP(object));
472 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
473 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
474 _layer_hierarchy->setBottom(object);
475 }
477 void SPDesktop::toggleLayerSolo(SPObject *object) {
478 g_return_if_fail(SP_IS_GROUP(object));
479 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
481 bool othersShowing = false;
482 std::vector<SPObject*> layers;
483 for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
484 layers.push_back(obj);
485 othersShowing |= !SP_ITEM(obj)->isHidden();
486 }
487 for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
488 layers.push_back(obj);
489 othersShowing |= !SP_ITEM(obj)->isHidden();
490 }
493 if ( SP_ITEM(object)->isHidden() ) {
494 SP_ITEM(object)->setHidden(false);
495 }
497 for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
498 SP_ITEM(*it)->setHidden(othersShowing);
499 }
500 }
502 /**
503 * Return layer that contains \a object.
504 */
505 SPObject *SPDesktop::layerForObject(SPObject *object) {
506 g_return_val_if_fail(object != NULL, NULL);
508 SPObject *root=currentRoot();
509 object = SP_OBJECT_PARENT(object);
510 while ( object && object != root && !isLayer(object) ) {
511 object = SP_OBJECT_PARENT(object);
512 }
513 return object;
514 }
516 /**
517 * True if object is a layer.
518 */
519 bool SPDesktop::isLayer(SPObject *object) const {
520 return ( SP_IS_GROUP(object)
521 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
522 == SPGroup::LAYER ) );
523 }
525 /**
526 * True if desktop viewport fully contains \a item's bbox.
527 */
528 bool SPDesktop::isWithinViewport (SPItem *item) const
529 {
530 Geom::Rect const viewport = get_display_area();
531 boost::optional<NR::Rect> const bbox = sp_item_bbox_desktop(item);
532 if (bbox) {
533 return viewport.contains(to_2geom(*bbox));
534 } else {
535 return true;
536 }
537 }
539 ///
540 bool SPDesktop::itemIsHidden(SPItem const *item) const {
541 return item->isHidden(this->dkey);
542 }
544 /**
545 * Set activate property of desktop; emit signal if changed.
546 */
547 void
548 SPDesktop::set_active (bool new_active)
549 {
550 if (new_active != _active) {
551 _active = new_active;
552 if (new_active) {
553 _activate_signal.emit();
554 } else {
555 _deactivate_signal.emit();
556 }
557 }
558 }
560 /**
561 * Set activate status of current desktop's named view.
562 */
563 void
564 SPDesktop::activate_guides(bool activate)
565 {
566 guides_active = activate;
567 namedview->activateGuides(this, activate);
568 }
570 /**
571 * Make desktop switch documents.
572 */
573 void
574 SPDesktop::change_document (SPDocument *theDocument)
575 {
576 g_return_if_fail (theDocument != NULL);
578 /* unselect everything before switching documents */
579 selection->clear();
581 setDocument (theDocument);
583 /* update the rulers, connect the desktop widget's signal to the new namedview etc.
584 (this can probably be done in a better way) */
585 Gtk::Window *parent = this->getToplevel();
586 g_assert(parent != NULL);
587 SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
588 if (dtw) dtw->desktop = this;
589 sp_desktop_widget_update_namedview(dtw);
591 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
592 _document_replaced_signal.emit (this, theDocument);
593 }
595 /**
596 * Make desktop switch event contexts.
597 */
598 void
599 SPDesktop::set_event_context (GtkType type, const gchar *config)
600 {
601 SPEventContext *ec;
602 while (event_context) {
603 ec = event_context;
604 sp_event_context_deactivate (ec);
605 event_context = ec->next;
606 sp_event_context_finish (ec);
607 g_object_unref (G_OBJECT (ec));
608 }
610 Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
611 ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
612 ec->next = event_context;
613 event_context = ec;
614 sp_event_context_activate (ec);
615 _event_context_changed_signal.emit (this, ec);
616 }
618 /**
619 * Push event context onto desktop's context stack.
620 */
621 void
622 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
623 {
624 SPEventContext *ref, *ec;
625 Inkscape::XML::Node *repr;
627 if (event_context && event_context->key == key) return;
628 ref = event_context;
629 while (ref && ref->next && ref->next->key != key) ref = ref->next;
630 if (ref && ref->next) {
631 ec = ref->next;
632 ref->next = ec->next;
633 sp_event_context_finish (ec);
634 g_object_unref (G_OBJECT (ec));
635 }
637 if (event_context) sp_event_context_deactivate (event_context);
638 repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
639 ec = sp_event_context_new (type, this, repr, key);
640 ec->next = event_context;
641 event_context = ec;
642 sp_event_context_activate (ec);
643 _event_context_changed_signal.emit (this, ec);
644 }
646 /**
647 * Sets the coordinate status to a given point
648 */
649 void
650 SPDesktop::set_coordinate_status (Geom::Point p) {
651 _widget->setCoordinateStatus(p);
652 }
654 /**
655 * \see sp_document_item_from_list_at_point_bottom()
656 */
657 SPItem *
658 SPDesktop::item_from_list_at_point_bottom (const GSList *list, Geom::Point const p) const
659 {
660 g_return_val_if_fail (doc() != NULL, NULL);
661 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
662 }
664 /**
665 * \see sp_document_item_at_point()
666 */
667 SPItem *
668 SPDesktop::item_at_point (Geom::Point const p, bool into_groups, SPItem *upto) const
669 {
670 g_return_val_if_fail (doc() != NULL, NULL);
671 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
672 }
674 /**
675 * \see sp_document_group_at_point()
676 */
677 SPItem *
678 SPDesktop::group_at_point (Geom::Point const p) const
679 {
680 g_return_val_if_fail (doc() != NULL, NULL);
681 return sp_document_group_at_point (doc(), dkey, p);
682 }
684 /**
685 * \brief Returns the mouse point in document coordinates; if mouse is
686 * outside the canvas, returns the center of canvas viewpoint
687 */
688 Geom::Point
689 SPDesktop::point() const
690 {
691 Geom::Point p = _widget->getPointer();
692 Geom::Point pw = sp_canvas_window_to_world (canvas, p);
693 p = w2d(pw);
695 Geom::Rect const r = canvas->getViewbox();
697 Geom::Point r0 = w2d(r.min());
698 Geom::Point r1 = w2d(r.max());
700 if (p[Geom::X] >= r0[Geom::X] &&
701 p[Geom::X] <= r1[Geom::X] &&
702 p[Geom::Y] >= r1[Geom::Y] &&
703 p[Geom::Y] <= r0[Geom::Y])
704 {
705 return p;
706 } else {
707 return (r0 + r1) / 2;
708 }
709 }
711 /**
712 * Put current zoom data in history list.
713 */
714 void
715 SPDesktop::push_current_zoom (GList **history)
716 {
717 Geom::Rect const area = get_display_area();
719 NRRect *old_zoom = g_new(NRRect, 1);
720 old_zoom->x0 = area.min()[NR::X];
721 old_zoom->x1 = area.max()[NR::X];
722 old_zoom->y0 = area.min()[NR::Y];
723 old_zoom->y1 = area.max()[NR::Y];
724 if ( *history == NULL
725 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
726 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
727 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
728 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
729 {
730 *history = g_list_prepend (*history, old_zoom);
731 }
732 }
734 /**
735 * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
736 */
737 void
738 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
739 {
740 g_assert(_widget);
742 // save the zoom
743 if (log) {
744 push_current_zoom(&zooms_past);
745 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
746 g_list_free (zooms_future);
747 zooms_future = NULL;
748 }
750 double const cx = 0.5 * (x0 + x1);
751 double const cy = 0.5 * (y0 + y1);
753 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
754 Geom::Rect viewbox = canvas->getViewbox();
755 viewbox.expandBy(border);
757 double scale = _d2w.descrim();
758 double newscale;
759 if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) {
760 newscale = viewbox.dimensions()[Geom::X] / (x1 - x0);
761 } else {
762 newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0);
763 }
765 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
767 int clear = FALSE;
768 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
769 /* Set zoom factors */
770 _d2w = NR::Matrix(NR::scale(newscale, -newscale));
771 _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
772 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
773 clear = TRUE;
774 }
776 /* Calculate top left corner (in document pixels) */
777 x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
778 y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
780 /* Scroll */
781 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
783 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
784 sp_box3d_context_update_lines(event_context);
786 _widget->updateRulers();
787 _widget->updateScrollbars(_d2w.descrim());
788 _widget->updateZoom();
789 }
791 void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log)
792 {
793 set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log);
794 }
796 /**
797 * Return viewbox dimensions.
798 */
799 Geom::Rect SPDesktop::get_display_area() const
800 {
801 Geom::Rect const viewbox = canvas->getViewbox();
803 double const scale = _d2w[0];
805 return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale),
806 Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale));
807 }
809 /**
810 * Revert back to previous zoom if possible.
811 */
812 void
813 SPDesktop::prev_zoom()
814 {
815 if (zooms_past == NULL) {
816 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
817 return;
818 }
820 // push current zoom into forward zooms list
821 push_current_zoom (&zooms_future);
823 // restore previous zoom
824 set_display_area (((NRRect *) zooms_past->data)->x0,
825 ((NRRect *) zooms_past->data)->y0,
826 ((NRRect *) zooms_past->data)->x1,
827 ((NRRect *) zooms_past->data)->y1,
828 0, false);
830 // remove the just-added zoom from the past zooms list
831 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
832 }
834 /**
835 * Set zoom to next in list.
836 */
837 void
838 SPDesktop::next_zoom()
839 {
840 if (zooms_future == NULL) {
841 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
842 return;
843 }
845 // push current zoom into past zooms list
846 push_current_zoom (&zooms_past);
848 // restore next zoom
849 set_display_area (((NRRect *) zooms_future->data)->x0,
850 ((NRRect *) zooms_future->data)->y0,
851 ((NRRect *) zooms_future->data)->x1,
852 ((NRRect *) zooms_future->data)->y1,
853 0, false);
855 // remove the just-used zoom from the zooms_future list
856 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
857 }
859 /**
860 * Zoom to point with absolute zoom factor.
861 */
862 void
863 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
864 {
865 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
867 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
868 // this check prevents "sliding" when trying to zoom in at maximum zoom;
869 /// \todo someone please fix calculations properly and remove this hack
870 if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
871 return;
873 Geom::Rect const viewbox = canvas->getViewbox();
875 double const width2 = viewbox.dimensions()[NR::X] / zoom;
876 double const height2 = viewbox.dimensions()[NR::Y] / zoom;
878 set_display_area(cx - px * width2,
879 cy - py * height2,
880 cx + (1 - px) * width2,
881 cy + (1 - py) * height2,
882 0.0);
883 }
885 /**
886 * Zoom to center with absolute zoom factor.
887 */
888 void
889 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
890 {
891 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
892 }
894 /**
895 * Zoom to point with relative zoom factor.
896 */
897 void
898 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
899 {
900 Geom::Rect const area = get_display_area();
902 if (cx < area.min()[NR::X]) {
903 cx = area.min()[NR::X];
904 }
905 if (cx > area.max()[NR::X]) {
906 cx = area.max()[NR::X];
907 }
908 if (cy < area.min()[NR::Y]) {
909 cy = area.min()[NR::Y];
910 }
911 if (cy > area.max()[NR::Y]) {
912 cy = area.max()[NR::Y];
913 }
915 gdouble const scale = _d2w.descrim() * zoom;
916 double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
917 double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
919 zoom_absolute_keep_point(cx, cy, px, py, scale);
920 }
922 /**
923 * Zoom to center with relative zoom factor.
924 */
925 void
926 SPDesktop::zoom_relative (double cx, double cy, double zoom)
927 {
928 gdouble scale = _d2w.descrim() * zoom;
929 zoom_absolute (cx, cy, scale);
930 }
932 /**
933 * Set display area to origin and current document dimensions.
934 */
935 void
936 SPDesktop::zoom_page()
937 {
938 Geom::Rect d(Geom::Point(0, 0),
939 Geom::Point(sp_document_width(doc()), sp_document_height(doc())));
941 // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 1.0; is it safe to ignore it?
942 if (d.isEmpty()) {
943 return;
944 }
946 set_display_area(d, 10);
947 }
949 /**
950 * Set display area to current document width.
951 */
952 void
953 SPDesktop::zoom_page_width()
954 {
955 Geom::Rect const a = get_display_area();
957 if (sp_document_width(doc()) < 1.0) {
958 return;
959 }
961 Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]),
962 Geom::Point(sp_document_width(doc()), a.midpoint()[Geom::Y]));
964 set_display_area(d, 10);
965 }
967 /**
968 * Zoom to selection.
969 */
970 void
971 SPDesktop::zoom_selection()
972 {
973 boost::optional<Geom::Rect> const d = to_2geom(selection->bounds());
975 // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 0.1; is it safe to ignore it?
976 if ( !d || d->isEmpty() ) {
977 return;
978 }
980 set_display_area(*d, 10);
981 }
983 /**
984 * Tell widget to let zoom widget grab keyboard focus.
985 */
986 void
987 SPDesktop::zoom_grab_focus()
988 {
989 _widget->letZoomGrabFocus();
990 }
992 /**
993 * Zoom to whole drawing.
994 */
995 void
996 SPDesktop::zoom_drawing()
997 {
998 g_return_if_fail (doc() != NULL);
999 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
1000 g_return_if_fail (docitem != NULL);
1002 boost::optional<Geom::Rect> d = to_2geom(sp_item_bbox_desktop(docitem));
1004 /* Note that the second condition here indicates that
1005 ** there are no items in the drawing.
1006 */
1007 // FIXME: the original NR::Rect::isEmpty call contained an additional threshold of 1.0; is it safe to ignore it?
1008 if ( !d || d->isEmpty() ) {
1009 return;
1010 }
1012 set_display_area(*d, 10);
1013 }
1015 /**
1016 * Scroll canvas by specific coordinate amount in svg coordinates.
1017 */
1018 void
1019 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1020 {
1021 double scale = _d2w.descrim();
1022 scroll_world(dx*scale, dy*scale, is_scrolling);
1023 }
1025 /**
1026 * Scroll canvas by specific coordinate amount.
1027 */
1028 void
1029 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1030 {
1031 g_assert(_widget);
1033 Geom::Rect const viewbox = canvas->getViewbox();
1035 sp_canvas_scroll_to(canvas, viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling);
1037 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1038 sp_box3d_context_update_lines(event_context);
1040 _widget->updateRulers();
1041 _widget->updateScrollbars(_d2w.descrim());
1042 }
1044 bool
1045 SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed)
1046 {
1047 gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
1049 // autoscrolldistance is in screen pixels, but the display area is in document units
1050 autoscrolldistance /= _d2w.descrim();
1051 // FIXME: This 2geom idiom doesn't allow us to declare dbox const
1052 Geom::Rect dbox = get_display_area();
1053 dbox.expandBy(-autoscrolldistance);
1055 if (!(p[NR::X] > dbox.min()[NR::X] && p[NR::X] < dbox.max()[NR::X]) ||
1056 !(p[NR::Y] > dbox.min()[NR::Y] && p[NR::Y] < dbox.max()[NR::Y]) ) {
1058 Geom::Point const s_w( p * (Geom::Matrix)_d2w );
1060 gdouble x_to;
1061 if (p[NR::X] < dbox.min()[NR::X])
1062 x_to = dbox.min()[NR::X];
1063 else if (p[NR::X] > dbox.max()[NR::X])
1064 x_to = dbox.max()[NR::X];
1065 else
1066 x_to = p[NR::X];
1068 gdouble y_to;
1069 if (p[NR::Y] < dbox.min()[NR::Y])
1070 y_to = dbox.min()[NR::Y];
1071 else if (p[NR::Y] > dbox.max()[NR::Y])
1072 y_to = dbox.max()[NR::Y];
1073 else
1074 y_to = p[NR::Y];
1076 Geom::Point const d_dt(x_to, y_to);
1077 Geom::Point const d_w( d_dt * _d2w );
1078 Geom::Point const moved_w( d_w - s_w );
1080 if (autoscrollspeed == 0)
1081 autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
1083 if (autoscrollspeed != 0)
1084 scroll_world (autoscrollspeed * moved_w);
1086 return true;
1087 }
1088 return false;
1089 }
1091 bool
1092 SPDesktop::is_iconified()
1093 {
1094 return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1095 }
1097 void
1098 SPDesktop::iconify()
1099 {
1100 _widget->setIconified();
1101 }
1103 bool
1104 SPDesktop::is_maximized()
1105 {
1106 return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1107 }
1109 void
1110 SPDesktop::maximize()
1111 {
1112 _widget->setMaximized();
1113 }
1115 bool
1116 SPDesktop::is_fullscreen()
1117 {
1118 return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1119 }
1121 void
1122 SPDesktop::fullscreen()
1123 {
1124 _widget->setFullscreen();
1125 }
1127 void
1128 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1129 {
1130 _widget->getGeometry (x, y, w, h);
1131 }
1133 void
1134 SPDesktop::setWindowPosition (Geom::Point p)
1135 {
1136 _widget->setPosition (p);
1137 }
1139 void
1140 SPDesktop::setWindowSize (gint w, gint h)
1141 {
1142 _widget->setSize (w, h);
1143 }
1145 void
1146 SPDesktop::setWindowTransient (void *p, int transient_policy)
1147 {
1148 _widget->setTransient (p, transient_policy);
1149 }
1151 Gtk::Window*
1152 SPDesktop::getToplevel( )
1153 {
1154 return _widget->getWindow();
1155 }
1157 void
1158 SPDesktop::presentWindow()
1159 {
1160 _widget->present();
1161 }
1163 bool
1164 SPDesktop::warnDialog (gchar *text)
1165 {
1166 return _widget->warnDialog (text);
1167 }
1169 void
1170 SPDesktop::toggleRulers()
1171 {
1172 _widget->toggleRulers();
1173 }
1175 void
1176 SPDesktop::toggleScrollbars()
1177 {
1178 _widget->toggleScrollbars();
1179 }
1181 void
1182 SPDesktop::layoutWidget()
1183 {
1184 _widget->layout();
1185 }
1187 void
1188 SPDesktop::destroyWidget()
1189 {
1190 _widget->destroy();
1191 }
1193 bool
1194 SPDesktop::shutdown()
1195 {
1196 return _widget->shutdown();
1197 }
1199 bool SPDesktop::onDeleteUI (GdkEventAny*)
1200 {
1201 if(shutdown())
1202 return true;
1204 destroyWidget();
1205 return false;
1206 }
1208 /**
1209 * onWindowStateEvent
1210 *
1211 * Called when the window changes its maximize/fullscreen/iconify/pinned state.
1212 * Since GTK doesn't have a way to query this state information directly, we
1213 * record it for the desktop here, and also possibly trigger a layout.
1214 */
1215 bool
1216 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1217 {
1218 // Record the desktop window's state
1219 window_state = event->new_window_state;
1221 // Layout may differ depending on full-screen mode or not
1222 GdkWindowState changed = event->changed_mask;
1223 if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1224 layoutWidget();
1225 }
1227 return false;
1228 }
1230 void
1231 SPDesktop::setToolboxFocusTo (gchar const *label)
1232 {
1233 _widget->setToolboxFocusTo (label);
1234 }
1236 void
1237 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1238 {
1239 _widget->setToolboxAdjustmentValue (id, val);
1240 }
1242 void
1243 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1244 {
1245 _widget->setToolboxSelectOneValue (id, val);
1246 }
1248 bool
1249 SPDesktop::isToolboxButtonActive (gchar const *id)
1250 {
1251 return _widget->isToolboxButtonActive (id);
1252 }
1254 void
1255 SPDesktop::emitToolSubselectionChanged(gpointer data)
1256 {
1257 _tool_subselection_changed.emit(data);
1258 inkscape_subselection_changed (this);
1259 }
1261 void
1262 SPDesktop::updateNow()
1263 {
1264 sp_canvas_update_now(canvas);
1265 }
1267 void
1268 SPDesktop::enableInteraction()
1269 {
1270 _widget->enableInteraction();
1271 }
1273 void SPDesktop::disableInteraction()
1274 {
1275 _widget->disableInteraction();
1276 }
1278 void SPDesktop::setWaitingCursor()
1279 {
1280 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1281 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1282 gdk_cursor_unref(waiting);
1283 // GDK needs the flush for the cursor change to take effect
1284 gdk_flush();
1285 waiting_cursor = true;
1286 }
1288 void SPDesktop::clearWaitingCursor()
1289 {
1290 if (waiting_cursor)
1291 sp_event_context_update_cursor(sp_desktop_event_context(this));
1292 }
1294 void SPDesktop::toggleColorProfAdjust()
1295 {
1296 _widget->toggleColorProfAdjust();
1297 }
1299 void SPDesktop::toggleGrids()
1300 {
1301 if (namedview->grids) {
1302 if(gridgroup) {
1303 showGrids(!grids_visible);
1304 }
1305 } else {
1306 //there is no grid present at the moment. add a rectangular grid and make it visible
1307 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1308 Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1309 showGrids(true);
1310 }
1311 }
1313 void SPDesktop::showGrids(bool show, bool dirty_document)
1314 {
1315 grids_visible = show;
1316 sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1317 if (show) {
1318 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1319 } else {
1320 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1321 }
1322 }
1324 void SPDesktop::toggleSnapping()
1325 {
1326 bool v = namedview->snap_manager.getSnapEnabledGlobally();
1327 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1328 sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1329 }
1331 //----------------------------------------------------------------------
1332 // Callback implementations. The virtual ones are connected by the view.
1334 void
1335 SPDesktop::onPositionSet (double x, double y)
1336 {
1337 _widget->viewSetPosition (Geom::Point(x,y));
1338 }
1340 void
1341 SPDesktop::onResized (double /*x*/, double /*y*/)
1342 {
1343 // Nothing called here
1344 }
1346 /**
1347 * Redraw callback; queues Gtk redraw; connected by View.
1348 */
1349 void
1350 SPDesktop::onRedrawRequested ()
1351 {
1352 if (main) {
1353 _widget->requestCanvasUpdate();
1354 }
1355 }
1357 void
1358 SPDesktop::updateCanvasNow()
1359 {
1360 _widget->requestCanvasUpdateAndWait();
1361 }
1363 /**
1364 * Associate document with desktop.
1365 */
1366 void
1367 SPDesktop::setDocument (SPDocument *doc)
1368 {
1369 if (this->doc() && doc) {
1370 namedview->hide(this);
1371 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1372 }
1374 if (_layer_hierarchy) {
1375 _layer_hierarchy->clear();
1376 delete _layer_hierarchy;
1377 }
1378 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1379 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1380 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1381 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1382 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1384 /* setup EventLog */
1385 event_log = new Inkscape::EventLog(doc);
1386 doc->addUndoObserver(*event_log);
1388 _commit_connection.disconnect();
1389 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1391 /// \todo fixme: This condition exists to make sure the code
1392 /// inside is NOT called on initialization, only on replacement. But there
1393 /// are surely more safe methods to accomplish this.
1394 // TODO since the comment had reversed logic, check the intent of this block of code:
1395 if (drawing) {
1396 NRArenaItem *ai = 0;
1398 namedview = sp_document_namedview (doc, NULL);
1399 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1400 number = namedview->getViewCount();
1402 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1403 SP_CANVAS_ARENA (drawing)->arena,
1404 dkey,
1405 SP_ITEM_SHOW_DISPLAY);
1406 if (ai) {
1407 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1408 }
1409 namedview->show(this);
1410 /* Ugly hack */
1411 activate_guides (true);
1412 /* Ugly hack */
1413 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1414 }
1416 _document_replaced_signal.emit (this, doc);
1418 View::setDocument (doc);
1419 }
1421 void
1422 SPDesktop::onStatusMessage
1423 (Inkscape::MessageType type, gchar const *message)
1424 {
1425 if (_widget) {
1426 _widget->setMessage(type, message);
1427 }
1428 }
1430 void
1431 SPDesktop::onDocumentURISet (gchar const* uri)
1432 {
1433 _widget->setTitle(uri);
1434 }
1436 /**
1437 * Resized callback.
1438 */
1439 void
1440 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1441 {
1442 _doc2dt[5] = height;
1443 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1444 Geom::Rect const a(Geom::Point(0, 0), Geom::Point(width, height));
1445 SP_CTRLRECT(page)->setRectangle(a);
1446 SP_CTRLRECT(page_border)->setRectangle(a);
1447 }
1450 void
1451 SPDesktop::_onActivate (SPDesktop* dt)
1452 {
1453 if (!dt->_widget) return;
1454 dt->_widget->activateDesktop();
1455 }
1457 void
1458 SPDesktop::_onDeactivate (SPDesktop* dt)
1459 {
1460 if (!dt->_widget) return;
1461 dt->_widget->deactivateDesktop();
1462 }
1464 void
1465 SPDesktop::_onSelectionModified
1466 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1467 {
1468 if (!dt->_widget) return;
1469 dt->_widget->updateScrollbars (dt->_d2w.descrim());
1470 }
1472 static void
1473 _onSelectionChanged
1474 (Inkscape::Selection *selection, SPDesktop *desktop)
1475 {
1476 /** \todo
1477 * only change the layer for single selections, or what?
1478 * This seems reasonable -- for multiple selections there can be many
1479 * different layers involved.
1480 */
1481 SPItem *item=selection->singleItem();
1482 if (item) {
1483 SPObject *layer=desktop->layerForObject(item);
1484 if ( layer && layer != desktop->currentLayer() ) {
1485 desktop->setCurrentLayer(layer);
1486 }
1487 }
1488 }
1490 /**
1491 * Calls event handler of current event context.
1492 * \param arena Unused
1493 * \todo fixme
1494 */
1495 static gint
1496 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1497 {
1498 if (ai) {
1499 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1500 return sp_event_context_item_handler (desktop->event_context, spi, event);
1501 } else {
1502 return sp_event_context_root_handler (desktop->event_context, event);
1503 }
1504 }
1506 static void
1507 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1508 g_return_if_fail(SP_IS_GROUP(layer));
1509 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1510 }
1512 /// Callback
1513 static void
1514 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1515 g_return_if_fail(SP_IS_GROUP(layer));
1516 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1517 }
1519 /// Callback
1520 static void
1521 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1522 SPDesktop *desktop)
1523 {
1524 desktop->_layer_changed_signal.emit (bottom);
1525 }
1527 /// Called when document is starting to be rebuilt.
1528 static void
1529 _reconstruction_start (SPDesktop * desktop)
1530 {
1531 // printf("Desktop, starting reconstruction\n");
1532 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1533 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1535 /*
1536 GSList const * selection_objs = desktop->selection->list();
1537 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1539 }
1540 */
1541 desktop->selection->clear();
1543 // printf("Desktop, starting reconstruction end\n");
1544 }
1546 /// Called when document rebuild is finished.
1547 static void
1548 _reconstruction_finish (SPDesktop * desktop)
1549 {
1550 // printf("Desktop, finishing reconstruction\n");
1551 if (desktop->_reconstruction_old_layer_id == NULL)
1552 return;
1554 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1555 if (newLayer != NULL)
1556 desktop->setCurrentLayer(newLayer);
1558 g_free(desktop->_reconstruction_old_layer_id);
1559 desktop->_reconstruction_old_layer_id = NULL;
1560 // printf("Desktop, finishing reconstruction end\n");
1561 return;
1562 }
1564 /**
1565 * Namedview_modified callback.
1566 */
1567 static void
1568 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1569 {
1570 SPNamedView *nv=SP_NAMEDVIEW(obj);
1572 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1574 /* Recalculate snap distances */
1575 /* FIXME: why is the desktop getting involved in setting up something
1576 ** that is entirely to do with the namedview?
1577 */
1578 _update_snap_distances (desktop);
1580 /* Show/hide page background */
1581 if (nv->pagecolor & 0xff) {
1582 sp_canvas_item_show (desktop->table);
1583 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1584 sp_canvas_item_move_to_z (desktop->table, 0);
1585 } else {
1586 sp_canvas_item_hide (desktop->table);
1587 }
1589 /* Show/hide page border */
1590 if (nv->showborder) {
1591 // show
1592 sp_canvas_item_show (desktop->page_border);
1593 // set color and shadow
1594 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1595 if (nv->pageshadow) {
1596 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1597 }
1598 // place in the z-order stack
1599 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1600 sp_canvas_item_move_to_z (desktop->page_border, 2);
1601 } else {
1602 int order = sp_canvas_item_order (desktop->page_border);
1603 int morder = sp_canvas_item_order (desktop->drawing);
1604 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1605 morder - order);
1606 }
1607 } else {
1608 sp_canvas_item_hide (desktop->page_border);
1609 if (nv->pageshadow) {
1610 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1611 }
1612 }
1614 /* Show/hide page shadow */
1615 if (nv->showpageshadow && nv->pageshadow) {
1616 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1617 } else {
1618 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1619 }
1621 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1622 (SP_RGBA32_R_U(nv->pagecolor) +
1623 SP_RGBA32_G_U(nv->pagecolor) +
1624 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1625 // the background color is light or transparent, use black outline
1626 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1627 } else { // use white outline
1628 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1629 }
1630 }
1631 }
1633 /**
1634 * Callback to reset snapper's distances.
1635 */
1636 static void
1637 _update_snap_distances (SPDesktop *desktop)
1638 {
1639 SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1641 SPNamedView &nv = *desktop->namedview;
1643 //tell all grid snappers
1644 for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1645 Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1646 grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
1647 *nv.gridtoleranceunit,
1648 px));
1649 }
1651 nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
1652 *nv.guidetoleranceunit,
1653 px));
1654 nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
1655 *nv.objecttoleranceunit,
1656 px));
1657 }
1660 Geom::Matrix SPDesktop::w2d() const
1661 {
1662 return _w2d;
1663 }
1665 Geom::Point SPDesktop::w2d(Geom::Point const &p) const
1666 {
1667 return p * _w2d;
1668 }
1670 Geom::Point SPDesktop::d2w(Geom::Point const &p) const
1671 {
1672 return p * _d2w;
1673 }
1675 Geom::Matrix SPDesktop::doc2dt() const
1676 {
1677 return _doc2dt;
1678 }
1680 Geom::Point SPDesktop::doc2dt(Geom::Point const &p) const
1681 {
1682 return p * _doc2dt;
1683 }
1685 Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const
1686 {
1687 return p * _doc2dt.inverse();
1688 }
1691 /**
1692 * Pop event context from desktop's context stack. Never used.
1693 */
1694 // void
1695 // SPDesktop::pop_event_context (unsigned int key)
1696 // {
1697 // SPEventContext *ec = NULL;
1698 //
1699 // if (event_context && event_context->key == key) {
1700 // g_return_if_fail (event_context);
1701 // g_return_if_fail (event_context->next);
1702 // ec = event_context;
1703 // sp_event_context_deactivate (ec);
1704 // event_context = ec->next;
1705 // sp_event_context_activate (event_context);
1706 // _event_context_changed_signal.emit (this, ec);
1707 // }
1708 //
1709 // SPEventContext *ref = event_context;
1710 // while (ref && ref->next && ref->next->key != key)
1711 // ref = ref->next;
1712 //
1713 // if (ref && ref->next) {
1714 // ec = ref->next;
1715 // ref->next = ec->next;
1716 // }
1717 //
1718 // if (ec) {
1719 // sp_event_context_finish (ec);
1720 // g_object_unref (G_OBJECT (ec));
1721 // }
1722 // }
1724 /*
1725 Local Variables:
1726 mode:c++
1727 c-file-style:"stroustrup"
1728 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1729 indent-tabs-mode:nil
1730 fill-column:99
1731 End:
1732 */
1733 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :