1 #define __SP_DESKTOP_C__
3 /** \file
4 * Editable view implementation
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * MenTaLguY <mental@rydia.net>
9 * bulia byak <buliabyak@users.sf.net>
10 * Ralf Stephan <ralf@ark.in-berlin.de>
11 * John Bintz <jcoswell@coswellproductions.org>
12 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
13 *
14 * Copyright (C) 2007 Jon A. Cruz
15 * Copyright (C) 2006-2008 Johan Engelen
16 * Copyright (C) 2006 John Bintz
17 * Copyright (C) 2004 MenTaLguY
18 * Copyright (C) 1999-2002 Lauris Kaplinski
19 * Copyright (C) 2000-2001 Ximian, Inc.
20 *
21 * Released under GNU GPL, read the file 'COPYING' for more information
22 */
24 /** \class SPDesktop
25 * SPDesktop is a subclass of View, implementing an editable document
26 * canvas. It is extensively used by many UI controls that need certain
27 * visual representations of their own.
28 *
29 * SPDesktop provides a certain set of SPCanvasItems, serving as GUI
30 * layers of different control objects. The one containing the whole
31 * document is the drawing layer. In addition to it, there are grid,
32 * guide, sketch and control layers. The sketch layer is used for
33 * temporary drawing objects, before the real objects in document are
34 * created. The control layer contains editing knots, rubberband and
35 * similar non-document UI objects.
36 *
37 * Each SPDesktop is associated with a SPNamedView node of the document
38 * tree. Currently, all desktops are created from a single main named
39 * view, but in the future there may be support for different ones.
40 * SPNamedView serves as an in-document container for desktop-related
41 * data, like grid and guideline placement, snapping options and so on.
42 *
43 * Associated with each SPDesktop are the two most important editing
44 * related objects - SPSelection and SPEventContext.
45 *
46 * Sodipodi keeps track of the active desktop and invokes notification
47 * signals whenever it changes. UI elements can use these to update their
48 * display to the selection of the currently active editing window.
49 * (Lauris Kaplinski)
50 */
52 #ifdef HAVE_CONFIG_H
53 # include "config.h"
54 #endif
56 #include <glibmm/i18n.h>
57 #include <sigc++/functors/mem_fun.h>
58 #include <gtkmm.h>
60 #include "macros.h"
61 #include "inkscape-private.h"
62 #include "desktop.h"
63 #include "desktop-events.h"
64 #include "desktop-handles.h"
65 #include "document.h"
66 #include "message-stack.h"
67 #include "selection.h"
68 #include "select-context.h"
69 #include "sp-namedview.h"
70 #include "color.h"
71 #include "sp-item-group.h"
72 #include "prefs-utils.h"
73 #include "object-hierarchy.h"
74 #include "helper/units.h"
75 #include "display/canvas-arena.h"
76 #include "display/nr-arena.h"
77 #include "display/gnome-canvas-acetate.h"
78 #include "display/sodipodi-ctrlrect.h"
79 #include "display/sp-canvas-util.h"
80 #include "display/canvas-temporary-item-list.h"
81 #include "display/snap-indicator.h"
82 #include "libnr/nr-matrix-div.h"
83 #include "libnr/nr-rect-ops.h"
84 #include "ui/dialog/dialog-manager.h"
85 #include "xml/repr.h"
86 #include "message-context.h"
87 #include "device-manager.h"
88 #include "layer-fns.h"
89 #include "layer-manager.h"
90 #include "event-log.h"
91 #include "display/canvas-grid.h"
92 #include "widgets/desktop-widget.h"
93 #include "box3d-context.h"
95 #include "display/sp-canvas.h"
97 namespace Inkscape { namespace XML { class Node; }}
99 // Callback declarations
100 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
101 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
102 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
103 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
104 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
105 static void _reconstruction_start(SPDesktop * desktop);
106 static void _reconstruction_finish(SPDesktop * desktop);
107 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
108 static void _update_snap_distances (SPDesktop *desktop);
110 /**
111 * Return new desktop object.
112 * \pre namedview != NULL.
113 * \pre canvas != NULL.
114 */
115 SPDesktop::SPDesktop() :
116 _dlg_mgr( 0 ),
117 namedview( 0 ),
118 canvas( 0 ),
119 selection( 0 ),
120 event_context( 0 ),
121 layer_manager( 0 ),
122 event_log( 0 ),
123 temporary_item_list( 0 ),
124 snapindicator( 0 ),
125 acetate( 0 ),
126 main( 0 ),
127 gridgroup( 0 ),
128 guides( 0 ),
129 drawing( 0 ),
130 sketch( 0 ),
131 controls( 0 ),
132 tempgroup ( 0 ),
133 table( 0 ),
134 page( 0 ),
135 page_border( 0 ),
136 current( 0 ),
137 zooms_past( 0 ),
138 zooms_future( 0 ),
139 dkey( 0 ),
140 number( 0 ),
141 window_state(0),
142 interaction_disabled_counter( 0 ),
143 waiting_cursor( false ),
144 guides_active( false ),
145 gr_item( 0 ),
146 gr_point_type( 0 ),
147 gr_point_i( 0 ),
148 gr_fill_or_stroke( true ),
149 _layer_hierarchy( 0 ),
150 _reconstruction_old_layer_id( 0 ),
151 _display_mode(Inkscape::RENDERMODE_NORMAL),
152 _saved_display_mode(Inkscape::RENDERMODE_NORMAL),
153 _widget( 0 ),
154 _inkscape( 0 ),
155 _guides_message_context( 0 ),
156 _active( false ),
157 _w2d(),
158 _d2w(),
159 _doc2dt( NR::Matrix(NR::scale(1, -1)) ),
160 grids_visible( false )
161 {
162 _d2w.set_identity();
163 _w2d.set_identity();
165 selection = Inkscape::GC::release( new Inkscape::Selection(this) );
166 }
168 void
169 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
170 {
171 // Temporary workaround for link order issues:
172 Inkscape::DeviceManager::getManager().getDevices();
174 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
176 current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
178 namedview = nv;
179 canvas = aCanvas;
181 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
182 /* Kill flicker */
183 sp_document_ensure_up_to_date (document);
185 /* Setup Dialog Manager */
186 _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
188 dkey = sp_item_display_key_new (1);
190 /* Connect document */
191 setDocument (document);
193 number = namedview->getViewCount();
196 /* Setup Canvas */
197 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
199 SPCanvasGroup *root = sp_canvas_root (canvas);
201 /* Setup adminstrative layers */
202 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
203 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
204 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
205 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
207 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
208 SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
209 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
210 sp_canvas_item_move_to_z (table, 0);
212 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
213 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
214 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
216 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
217 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
219 SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
221 if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
222 // Start in outline mode
223 setDisplayModeOutline();
224 } else {
225 // Start in normal mode, default
226 setDisplayModeNormal();
227 }
229 gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
230 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
231 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
232 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
233 tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
235 /* Push select tool to the bottom of stack */
236 /** \todo
237 * FIXME: this is the only call to this. Everything else seems to just
238 * call "set" instead of "push". Can we assume that there is only one
239 * context ever?
240 */
241 push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
243 // display rect and zoom are now handled in sp_desktop_widget_realize()
245 NR::Rect const d(NR::Point(0.0, 0.0),
246 NR::Point(sp_document_width(document), sp_document_height(document)));
248 SP_CTRLRECT(page)->setRectangle(d);
249 SP_CTRLRECT(page_border)->setRectangle(d);
251 /* the following sets the page shadow on the canvas
252 It was originally set to 5, which is really cheesy!
253 It now is an attribute in the document's namedview. If a value of
254 0 is used, then the constructor for a shadow is not initialized.
255 */
257 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
258 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
259 }
262 /* Connect event for page resize */
263 _doc2dt[5] = sp_document_height (document);
264 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
266 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
268 NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
269 SP_CANVAS_ARENA (drawing)->arena,
270 dkey,
271 SP_ITEM_SHOW_DISPLAY);
272 if (ai) {
273 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
274 nr_arena_item_unref (ai);
275 }
277 namedview->show(this);
278 /* Ugly hack */
279 activate_guides (true);
280 /* Ugly hack */
281 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
283 /* Set up notification of rebuilding the document, this allows
284 for saving object related settings in the document. */
285 _reconstruction_start_connection =
286 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
287 _reconstruction_finish_connection =
288 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
289 _reconstruction_old_layer_id = NULL;
291 // ?
292 // sp_active_desktop_set (desktop);
293 _inkscape = INKSCAPE;
295 _activate_connection = _activate_signal.connect(
296 sigc::bind(
297 sigc::ptr_fun(_onActivate),
298 this
299 )
300 );
301 _deactivate_connection = _deactivate_signal.connect(
302 sigc::bind(
303 sigc::ptr_fun(_onDeactivate),
304 this
305 )
306 );
308 _sel_modified_connection = selection->connectModified(
309 sigc::bind(
310 sigc::ptr_fun(&_onSelectionModified),
311 this
312 )
313 );
314 _sel_changed_connection = selection->connectChanged(
315 sigc::bind(
316 sigc::ptr_fun(&_onSelectionChanged),
317 this
318 )
319 );
322 /* setup LayerManager */
323 // (Setting up after the connections are all in place, as it may use some of them)
324 layer_manager = new Inkscape::LayerManager( this );
326 showGrids(namedview->grids_visible, false);
328 temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
329 snapindicator = new Inkscape::Display::SnapIndicator ( this );
330 }
333 void SPDesktop::destroy()
334 {
335 if (snapindicator) {
336 delete snapindicator;
337 snapindicator = NULL;
338 }
339 if (temporary_item_list) {
340 delete temporary_item_list;
341 temporary_item_list = NULL;
342 }
344 namedview->hide(this);
346 _activate_connection.disconnect();
347 _deactivate_connection.disconnect();
348 _sel_modified_connection.disconnect();
349 _sel_changed_connection.disconnect();
350 _modified_connection.disconnect();
351 _commit_connection.disconnect();
352 _reconstruction_start_connection.disconnect();
353 _reconstruction_finish_connection.disconnect();
355 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
356 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
357 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
359 while (event_context) {
360 SPEventContext *ec = event_context;
361 event_context = ec->next;
362 sp_event_context_finish (ec);
363 g_object_unref (G_OBJECT (ec));
364 }
366 if (_layer_hierarchy) {
367 delete _layer_hierarchy;
368 // _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
369 }
371 if (layer_manager) {
372 delete layer_manager;
373 layer_manager = NULL;
374 }
376 if (_inkscape) {
377 _inkscape = NULL;
378 }
380 if (drawing) {
381 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
382 drawing = NULL;
383 }
385 delete _guides_message_context;
386 _guides_message_context = NULL;
388 g_list_free (zooms_past);
389 g_list_free (zooms_future);
390 }
392 SPDesktop::~SPDesktop() {}
394 //--------------------------------------------------------------------
395 /* Public methods */
398 /* These methods help for temporarily showing things on-canvas.
399 * The *only* valid use of the TemporaryItem* that you get from add_temporary_canvasitem
400 * is when you want to prematurely remove the item from the canvas, by calling
401 * desktop->remove_temporary_canvasitem(tempitem).
402 */
403 /** Note that lifetime is measured in milliseconds
404 * One should *not* keep a reference to the SPCanvasItem, the temporary item code will
405 * delete the object for you and the reference will become invalid without you knowing it.
406 * It is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
407 * The *only* valid use of the returned TemporaryItem* is as argument for SPDesktop::remove_temporary_canvasitem,
408 * because the object might be deleted already without you knowing it.
409 * move_to_bottom = true by default so the item does not interfere with handling of other items on the canvas like nodes.
410 */
411 Inkscape::Display::TemporaryItem *
412 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime, bool move_to_bottom)
413 {
414 if (move_to_bottom) {
415 sp_canvas_item_move_to_z(item, 0);
416 }
418 return temporary_item_list->add_item(item, lifetime);
419 }
421 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
422 */
423 void
424 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
425 {
426 // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
427 if (tempitem && temporary_item_list) {
428 temporary_item_list->delete_item(tempitem);
429 }
430 }
432 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
433 SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
434 canvas->rendermode = mode;
435 _display_mode = mode;
436 if (mode != Inkscape::RENDERMODE_OUTLINE) {
437 _saved_display_mode = _display_mode;
438 }
439 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
440 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
441 }
443 void SPDesktop::displayModeToggle() {
444 if (_display_mode == Inkscape::RENDERMODE_OUTLINE) {
445 _setDisplayMode(_saved_display_mode);
446 } else {
447 _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
448 }
449 }
451 /**
452 * Returns current root (=bottom) layer.
453 */
454 SPObject *SPDesktop::currentRoot() const
455 {
456 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
457 }
459 /**
460 * Returns current top layer.
461 */
462 SPObject *SPDesktop::currentLayer() const
463 {
464 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
465 }
467 /**
468 * Sets the current layer of the desktop.
469 *
470 * Make \a object the top layer.
471 */
472 void SPDesktop::setCurrentLayer(SPObject *object) {
473 g_return_if_fail(SP_IS_GROUP(object));
474 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
475 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
476 _layer_hierarchy->setBottom(object);
477 }
479 void SPDesktop::toggleLayerSolo(SPObject *object) {
480 g_return_if_fail(SP_IS_GROUP(object));
481 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
483 bool othersShowing = false;
484 std::vector<SPObject*> layers;
485 for ( SPObject* obj = Inkscape::next_layer(currentRoot(), object); obj; obj = Inkscape::next_layer(currentRoot(), obj) ) {
486 layers.push_back(obj);
487 othersShowing |= !SP_ITEM(obj)->isHidden();
488 }
489 for ( SPObject* obj = Inkscape::previous_layer(currentRoot(), object); obj; obj = Inkscape::previous_layer(currentRoot(), obj) ) {
490 layers.push_back(obj);
491 othersShowing |= !SP_ITEM(obj)->isHidden();
492 }
495 if ( SP_ITEM(object)->isHidden() ) {
496 SP_ITEM(object)->setHidden(false);
497 }
499 for ( std::vector<SPObject*>::iterator it = layers.begin(); it != layers.end(); ++it ) {
500 SP_ITEM(*it)->setHidden(othersShowing);
501 }
502 }
504 /**
505 * Return layer that contains \a object.
506 */
507 SPObject *SPDesktop::layerForObject(SPObject *object) {
508 g_return_val_if_fail(object != NULL, NULL);
510 SPObject *root=currentRoot();
511 object = SP_OBJECT_PARENT(object);
512 while ( object && object != root && !isLayer(object) ) {
513 object = SP_OBJECT_PARENT(object);
514 }
515 return object;
516 }
518 /**
519 * True if object is a layer.
520 */
521 bool SPDesktop::isLayer(SPObject *object) const {
522 return ( SP_IS_GROUP(object)
523 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
524 == SPGroup::LAYER ) );
525 }
527 /**
528 * True if desktop viewport fully contains \a item's bbox.
529 */
530 bool SPDesktop::isWithinViewport (SPItem *item) const
531 {
532 NR::Rect const viewport = get_display_area();
533 NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
534 if (bbox) {
535 return viewport.contains(*bbox);
536 } else {
537 return true;
538 }
539 }
541 ///
542 bool SPDesktop::itemIsHidden(SPItem const *item) const {
543 return item->isHidden(this->dkey);
544 }
546 /**
547 * Set activate property of desktop; emit signal if changed.
548 */
549 void
550 SPDesktop::set_active (bool new_active)
551 {
552 if (new_active != _active) {
553 _active = new_active;
554 if (new_active) {
555 _activate_signal.emit();
556 } else {
557 _deactivate_signal.emit();
558 }
559 }
560 }
562 /**
563 * Set activate status of current desktop's named view.
564 */
565 void
566 SPDesktop::activate_guides(bool activate)
567 {
568 guides_active = activate;
569 namedview->activateGuides(this, activate);
570 }
572 /**
573 * Make desktop switch documents.
574 */
575 void
576 SPDesktop::change_document (SPDocument *theDocument)
577 {
578 g_return_if_fail (theDocument != NULL);
580 /* unselect everything before switching documents */
581 selection->clear();
583 setDocument (theDocument);
585 /* update the rulers, connect the desktop widget's signal to the new namedview etc.
586 (this can probably be done in a better way) */
587 Gtk::Window *parent = this->getToplevel();
588 g_assert(parent != NULL);
589 SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
590 if (dtw) dtw->desktop = this;
591 sp_desktop_widget_update_namedview(dtw);
593 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
594 _document_replaced_signal.emit (this, theDocument);
595 }
597 /**
598 * Make desktop switch event contexts.
599 */
600 void
601 SPDesktop::set_event_context (GtkType type, const gchar *config)
602 {
603 SPEventContext *ec;
604 while (event_context) {
605 ec = event_context;
606 sp_event_context_deactivate (ec);
607 event_context = ec->next;
608 sp_event_context_finish (ec);
609 g_object_unref (G_OBJECT (ec));
610 }
612 Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
613 ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
614 ec->next = event_context;
615 event_context = ec;
616 sp_event_context_activate (ec);
617 _event_context_changed_signal.emit (this, ec);
618 }
620 /**
621 * Push event context onto desktop's context stack.
622 */
623 void
624 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
625 {
626 SPEventContext *ref, *ec;
627 Inkscape::XML::Node *repr;
629 if (event_context && event_context->key == key) return;
630 ref = event_context;
631 while (ref && ref->next && ref->next->key != key) ref = ref->next;
632 if (ref && ref->next) {
633 ec = ref->next;
634 ref->next = ec->next;
635 sp_event_context_finish (ec);
636 g_object_unref (G_OBJECT (ec));
637 }
639 if (event_context) sp_event_context_deactivate (event_context);
640 repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
641 ec = sp_event_context_new (type, this, repr, key);
642 ec->next = event_context;
643 event_context = ec;
644 sp_event_context_activate (ec);
645 _event_context_changed_signal.emit (this, ec);
646 }
648 /**
649 * Sets the coordinate status to a given point
650 */
651 void
652 SPDesktop::set_coordinate_status (NR::Point p) {
653 _widget->setCoordinateStatus(p);
654 }
656 /**
657 * \see sp_document_item_from_list_at_point_bottom()
658 */
659 SPItem *
660 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
661 {
662 g_return_val_if_fail (doc() != NULL, NULL);
663 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
664 }
666 /**
667 * \see sp_document_item_at_point()
668 */
669 SPItem *
670 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
671 {
672 g_return_val_if_fail (doc() != NULL, NULL);
673 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
674 }
676 /**
677 * \see sp_document_group_at_point()
678 */
679 SPItem *
680 SPDesktop::group_at_point (NR::Point const p) const
681 {
682 g_return_val_if_fail (doc() != NULL, NULL);
683 return sp_document_group_at_point (doc(), dkey, p);
684 }
686 /**
687 * \brief Returns the mouse point in document coordinates; if mouse is
688 * outside the canvas, returns the center of canvas viewpoint
689 */
690 NR::Point
691 SPDesktop::point() const
692 {
693 NR::Point p = _widget->getPointer();
694 NR::Point pw = sp_canvas_window_to_world (canvas, p);
695 p = w2d(pw);
697 NR::Rect const r = canvas->getViewbox();
699 NR::Point r0 = w2d(r.min());
700 NR::Point r1 = w2d(r.max());
702 if (p[NR::X] >= r0[NR::X] &&
703 p[NR::X] <= r1[NR::X] &&
704 p[NR::Y] >= r1[NR::Y] &&
705 p[NR::Y] <= r0[NR::Y])
706 {
707 return p;
708 } else {
709 return (r0 + r1) / 2;
710 }
711 }
713 /**
714 * Put current zoom data in history list.
715 */
716 void
717 SPDesktop::push_current_zoom (GList **history)
718 {
719 NR::Rect const area = get_display_area();
721 NRRect *old_zoom = g_new(NRRect, 1);
722 old_zoom->x0 = area.min()[NR::X];
723 old_zoom->x1 = area.max()[NR::X];
724 old_zoom->y0 = area.min()[NR::Y];
725 old_zoom->y1 = area.max()[NR::Y];
726 if ( *history == NULL
727 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
728 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
729 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
730 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
731 {
732 *history = g_list_prepend (*history, old_zoom);
733 }
734 }
736 /**
737 * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
738 */
739 void
740 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
741 {
742 g_assert(_widget);
744 // save the zoom
745 if (log) {
746 push_current_zoom(&zooms_past);
747 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
748 g_list_free (zooms_future);
749 zooms_future = NULL;
750 }
752 double const cx = 0.5 * (x0 + x1);
753 double const cy = 0.5 * (y0 + y1);
755 NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
757 double scale = expansion(_d2w);
758 double newscale;
759 if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
760 newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
761 } else {
762 newscale = viewbox.dimensions()[NR::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(expansion(_d2w));
788 _widget->updateZoom();
789 }
791 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
792 {
793 set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
794 }
796 /**
797 * Return viewbox dimensions.
798 */
799 NR::Rect SPDesktop::get_display_area() const
800 {
801 NR::Rect const viewbox = canvas->getViewbox();
803 double const scale = _d2w[0];
805 return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
806 NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::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(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
871 return;
873 NR::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 NR::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 = expansion(_d2w) * 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 = expansion(_d2w) * 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 NR::Rect d(NR::Point(0, 0),
939 NR::Point(sp_document_width(doc()), sp_document_height(doc())));
941 if (d.isEmpty(1.0)) {
942 return;
943 }
945 set_display_area(d, 10);
946 }
948 /**
949 * Set display area to current document width.
950 */
951 void
952 SPDesktop::zoom_page_width()
953 {
954 NR::Rect const a = get_display_area();
956 if (sp_document_width(doc()) < 1.0) {
957 return;
958 }
960 NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
961 NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
963 set_display_area(d, 10);
964 }
966 /**
967 * Zoom to selection.
968 */
969 void
970 SPDesktop::zoom_selection()
971 {
972 NR::Maybe<NR::Rect> const d = selection->bounds();
974 if ( !d || d->isEmpty(0.1) ) {
975 return;
976 }
978 set_display_area(*d, 10);
979 }
981 /**
982 * Tell widget to let zoom widget grab keyboard focus.
983 */
984 void
985 SPDesktop::zoom_grab_focus()
986 {
987 _widget->letZoomGrabFocus();
988 }
990 /**
991 * Zoom to whole drawing.
992 */
993 void
994 SPDesktop::zoom_drawing()
995 {
996 g_return_if_fail (doc() != NULL);
997 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
998 g_return_if_fail (docitem != NULL);
1000 NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
1002 /* Note that the second condition here indicates that
1003 ** there are no items in the drawing.
1004 */
1005 if ( !d || d->isEmpty(1.0) ) {
1006 return;
1007 }
1009 set_display_area(*d, 10);
1010 }
1012 /**
1013 * Scroll canvas by specific coordinate amount in svg coordinates.
1014 */
1015 void
1016 SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling)
1017 {
1018 double scale = expansion(_d2w);
1019 scroll_world(dx*scale, dy*scale, is_scrolling);
1020 }
1022 /**
1023 * Scroll canvas by specific coordinate amount.
1024 */
1025 void
1026 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
1027 {
1028 g_assert(_widget);
1030 NR::Rect const viewbox = canvas->getViewbox();
1032 sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
1034 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
1035 sp_box3d_context_update_lines(event_context);
1037 _widget->updateRulers();
1038 _widget->updateScrollbars(expansion(_d2w));
1039 }
1041 bool
1042 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
1043 {
1044 gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
1046 // autoscrolldistance is in screen pixels, but the display area is in document units
1047 autoscrolldistance /= expansion(_d2w);
1048 NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
1050 if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
1051 !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y]) ) {
1053 NR::Point const s_w( (*p) * _d2w );
1055 gdouble x_to;
1056 if ((*p)[NR::X] < dbox.min()[NR::X])
1057 x_to = dbox.min()[NR::X];
1058 else if ((*p)[NR::X] > dbox.max()[NR::X])
1059 x_to = dbox.max()[NR::X];
1060 else
1061 x_to = (*p)[NR::X];
1063 gdouble y_to;
1064 if ((*p)[NR::Y] < dbox.min()[NR::Y])
1065 y_to = dbox.min()[NR::Y];
1066 else if ((*p)[NR::Y] > dbox.max()[NR::Y])
1067 y_to = dbox.max()[NR::Y];
1068 else
1069 y_to = (*p)[NR::Y];
1071 NR::Point const d_dt(x_to, y_to);
1072 NR::Point const d_w( d_dt * _d2w );
1073 NR::Point const moved_w( d_w - s_w );
1075 if (autoscrollspeed == 0)
1076 autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
1078 if (autoscrollspeed != 0)
1079 scroll_world (autoscrollspeed * moved_w);
1081 return true;
1082 }
1083 return false;
1084 }
1086 bool
1087 SPDesktop::is_iconified()
1088 {
1089 return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1090 }
1092 void
1093 SPDesktop::iconify()
1094 {
1095 _widget->setIconified();
1096 }
1098 bool
1099 SPDesktop::is_maximized()
1100 {
1101 return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1102 }
1104 void
1105 SPDesktop::maximize()
1106 {
1107 _widget->setMaximized();
1108 }
1110 bool
1111 SPDesktop::is_fullscreen()
1112 {
1113 return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1114 }
1116 void
1117 SPDesktop::fullscreen()
1118 {
1119 _widget->setFullscreen();
1120 }
1122 void
1123 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1124 {
1125 _widget->getGeometry (x, y, w, h);
1126 }
1128 void
1129 SPDesktop::setWindowPosition (NR::Point p)
1130 {
1131 _widget->setPosition (p);
1132 }
1134 void
1135 SPDesktop::setWindowSize (gint w, gint h)
1136 {
1137 _widget->setSize (w, h);
1138 }
1140 void
1141 SPDesktop::setWindowTransient (void *p, int transient_policy)
1142 {
1143 _widget->setTransient (p, transient_policy);
1144 }
1146 Gtk::Window*
1147 SPDesktop::getToplevel( )
1148 {
1149 return _widget->getWindow();
1150 }
1152 void
1153 SPDesktop::presentWindow()
1154 {
1155 _widget->present();
1156 }
1158 bool
1159 SPDesktop::warnDialog (gchar *text)
1160 {
1161 return _widget->warnDialog (text);
1162 }
1164 void
1165 SPDesktop::toggleRulers()
1166 {
1167 _widget->toggleRulers();
1168 }
1170 void
1171 SPDesktop::toggleScrollbars()
1172 {
1173 _widget->toggleScrollbars();
1174 }
1176 void
1177 SPDesktop::layoutWidget()
1178 {
1179 _widget->layout();
1180 }
1182 void
1183 SPDesktop::destroyWidget()
1184 {
1185 _widget->destroy();
1186 }
1188 bool
1189 SPDesktop::shutdown()
1190 {
1191 return _widget->shutdown();
1192 }
1194 bool SPDesktop::onDeleteUI (GdkEventAny*)
1195 {
1196 if(shutdown())
1197 return true;
1199 destroyWidget();
1200 return false;
1201 }
1203 /**
1204 * onWindowStateEvent
1205 *
1206 * Called when the window changes its maximize/fullscreen/iconify/pinned state.
1207 * Since GTK doesn't have a way to query this state information directly, we
1208 * record it for the desktop here, and also possibly trigger a layout.
1209 */
1210 bool
1211 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1212 {
1213 // Record the desktop window's state
1214 window_state = event->new_window_state;
1216 // Layout may differ depending on full-screen mode or not
1217 GdkWindowState changed = event->changed_mask;
1218 if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1219 layoutWidget();
1220 }
1222 return false;
1223 }
1225 void
1226 SPDesktop::setToolboxFocusTo (gchar const *label)
1227 {
1228 _widget->setToolboxFocusTo (label);
1229 }
1231 void
1232 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1233 {
1234 _widget->setToolboxAdjustmentValue (id, val);
1235 }
1237 void
1238 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1239 {
1240 _widget->setToolboxSelectOneValue (id, val);
1241 }
1243 bool
1244 SPDesktop::isToolboxButtonActive (gchar const *id)
1245 {
1246 return _widget->isToolboxButtonActive (id);
1247 }
1249 void
1250 SPDesktop::emitToolSubselectionChanged(gpointer data)
1251 {
1252 _tool_subselection_changed.emit(data);
1253 inkscape_subselection_changed (this);
1254 }
1256 void
1257 SPDesktop::updateNow()
1258 {
1259 sp_canvas_update_now(canvas);
1260 }
1262 void
1263 SPDesktop::enableInteraction()
1264 {
1265 _widget->enableInteraction();
1266 }
1268 void SPDesktop::disableInteraction()
1269 {
1270 _widget->disableInteraction();
1271 }
1273 void SPDesktop::setWaitingCursor()
1274 {
1275 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1276 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1277 gdk_cursor_unref(waiting);
1278 waiting_cursor = true;
1280 // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1281 // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1282 // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1283 // after the call to setWaitingCursor as it was before
1284 while( Gtk::Main::events_pending() )
1285 Gtk::Main::iteration();
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 (NR::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 nr_arena_item_unref (ai);
1409 }
1410 namedview->show(this);
1411 /* Ugly hack */
1412 activate_guides (true);
1413 /* Ugly hack */
1414 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1415 }
1417 _document_replaced_signal.emit (this, doc);
1419 View::setDocument (doc);
1420 }
1422 void
1423 SPDesktop::onStatusMessage
1424 (Inkscape::MessageType type, gchar const *message)
1425 {
1426 if (_widget) {
1427 _widget->setMessage(type, message);
1428 }
1429 }
1431 void
1432 SPDesktop::onDocumentURISet (gchar const* uri)
1433 {
1434 _widget->setTitle(uri);
1435 }
1437 /**
1438 * Resized callback.
1439 */
1440 void
1441 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1442 {
1443 _doc2dt[5] = height;
1444 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1445 NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1446 SP_CTRLRECT(page)->setRectangle(a);
1447 SP_CTRLRECT(page_border)->setRectangle(a);
1448 }
1451 void
1452 SPDesktop::_onActivate (SPDesktop* dt)
1453 {
1454 if (!dt->_widget) return;
1455 dt->_widget->activateDesktop();
1456 }
1458 void
1459 SPDesktop::_onDeactivate (SPDesktop* dt)
1460 {
1461 if (!dt->_widget) return;
1462 dt->_widget->deactivateDesktop();
1463 }
1465 void
1466 SPDesktop::_onSelectionModified
1467 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1468 {
1469 if (!dt->_widget) return;
1470 dt->_widget->updateScrollbars (expansion(dt->_d2w));
1471 }
1473 static void
1474 _onSelectionChanged
1475 (Inkscape::Selection *selection, SPDesktop *desktop)
1476 {
1477 /** \todo
1478 * only change the layer for single selections, or what?
1479 * This seems reasonable -- for multiple selections there can be many
1480 * different layers involved.
1481 */
1482 SPItem *item=selection->singleItem();
1483 if (item) {
1484 SPObject *layer=desktop->layerForObject(item);
1485 if ( layer && layer != desktop->currentLayer() ) {
1486 desktop->setCurrentLayer(layer);
1487 }
1488 }
1489 }
1491 /**
1492 * Calls event handler of current event context.
1493 * \param arena Unused
1494 * \todo fixme
1495 */
1496 static gint
1497 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1498 {
1499 if (ai) {
1500 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1501 return sp_event_context_item_handler (desktop->event_context, spi, event);
1502 } else {
1503 return sp_event_context_root_handler (desktop->event_context, event);
1504 }
1505 }
1507 static void
1508 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1509 g_return_if_fail(SP_IS_GROUP(layer));
1510 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1511 }
1513 /// Callback
1514 static void
1515 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1516 g_return_if_fail(SP_IS_GROUP(layer));
1517 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1518 }
1520 /// Callback
1521 static void
1522 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1523 SPDesktop *desktop)
1524 {
1525 desktop->_layer_changed_signal.emit (bottom);
1526 }
1528 /// Called when document is starting to be rebuilt.
1529 static void
1530 _reconstruction_start (SPDesktop * desktop)
1531 {
1532 // printf("Desktop, starting reconstruction\n");
1533 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1534 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1536 /*
1537 GSList const * selection_objs = desktop->selection->list();
1538 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1540 }
1541 */
1542 desktop->selection->clear();
1544 // printf("Desktop, starting reconstruction end\n");
1545 }
1547 /// Called when document rebuild is finished.
1548 static void
1549 _reconstruction_finish (SPDesktop * desktop)
1550 {
1551 // printf("Desktop, finishing reconstruction\n");
1552 if (desktop->_reconstruction_old_layer_id == NULL)
1553 return;
1555 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1556 if (newLayer != NULL)
1557 desktop->setCurrentLayer(newLayer);
1559 g_free(desktop->_reconstruction_old_layer_id);
1560 desktop->_reconstruction_old_layer_id = NULL;
1561 // printf("Desktop, finishing reconstruction end\n");
1562 return;
1563 }
1565 /**
1566 * Namedview_modified callback.
1567 */
1568 static void
1569 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1570 {
1571 SPNamedView *nv=SP_NAMEDVIEW(obj);
1573 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1575 /* Recalculate snap distances */
1576 /* FIXME: why is the desktop getting involved in setting up something
1577 ** that is entirely to do with the namedview?
1578 */
1579 _update_snap_distances (desktop);
1581 /* Show/hide page background */
1582 if (nv->pagecolor & 0xff) {
1583 sp_canvas_item_show (desktop->table);
1584 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1585 sp_canvas_item_move_to_z (desktop->table, 0);
1586 } else {
1587 sp_canvas_item_hide (desktop->table);
1588 }
1590 /* Show/hide page border */
1591 if (nv->showborder) {
1592 // show
1593 sp_canvas_item_show (desktop->page_border);
1594 // set color and shadow
1595 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1596 if (nv->pageshadow) {
1597 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1598 }
1599 // place in the z-order stack
1600 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1601 sp_canvas_item_move_to_z (desktop->page_border, 2);
1602 } else {
1603 int order = sp_canvas_item_order (desktop->page_border);
1604 int morder = sp_canvas_item_order (desktop->drawing);
1605 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1606 morder - order);
1607 }
1608 } else {
1609 sp_canvas_item_hide (desktop->page_border);
1610 if (nv->pageshadow) {
1611 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1612 }
1613 }
1615 /* Show/hide page shadow */
1616 if (nv->showpageshadow && nv->pageshadow) {
1617 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1618 } else {
1619 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1620 }
1622 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1623 (SP_RGBA32_R_U(nv->pagecolor) +
1624 SP_RGBA32_G_U(nv->pagecolor) +
1625 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1626 // the background color is light or transparent, use black outline
1627 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1628 } else { // use white outline
1629 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1630 }
1631 }
1632 }
1634 /**
1635 * Callback to reset snapper's distances.
1636 */
1637 static void
1638 _update_snap_distances (SPDesktop *desktop)
1639 {
1640 SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1642 SPNamedView &nv = *desktop->namedview;
1644 //tell all grid snappers
1645 for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1646 Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1647 grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
1648 *nv.gridtoleranceunit,
1649 px));
1650 }
1652 nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
1653 *nv.guidetoleranceunit,
1654 px));
1655 nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
1656 *nv.objecttoleranceunit,
1657 px));
1658 }
1661 NR::Matrix SPDesktop::w2d() const
1662 {
1663 return _w2d;
1664 }
1666 NR::Point SPDesktop::w2d(NR::Point const &p) const
1667 {
1668 return p * _w2d;
1669 }
1671 NR::Point SPDesktop::d2w(NR::Point const &p) const
1672 {
1673 return p * _d2w;
1674 }
1676 NR::Matrix SPDesktop::doc2dt() const
1677 {
1678 return _doc2dt;
1679 }
1681 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1682 {
1683 return p * _doc2dt;
1684 }
1686 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1687 {
1688 return p / _doc2dt;
1689 }
1692 /**
1693 * Pop event context from desktop's context stack. Never used.
1694 */
1695 // void
1696 // SPDesktop::pop_event_context (unsigned int key)
1697 // {
1698 // SPEventContext *ec = NULL;
1699 //
1700 // if (event_context && event_context->key == key) {
1701 // g_return_if_fail (event_context);
1702 // g_return_if_fail (event_context->next);
1703 // ec = event_context;
1704 // sp_event_context_deactivate (ec);
1705 // event_context = ec->next;
1706 // sp_event_context_activate (event_context);
1707 // _event_context_changed_signal.emit (this, ec);
1708 // }
1709 //
1710 // SPEventContext *ref = event_context;
1711 // while (ref && ref->next && ref->next->key != key)
1712 // ref = ref->next;
1713 //
1714 // if (ref && ref->next) {
1715 // ec = ref->next;
1716 // ref->next = ec->next;
1717 // }
1718 //
1719 // if (ec) {
1720 // sp_event_context_finish (ec);
1721 // g_object_unref (G_OBJECT (ec));
1722 // }
1723 // }
1725 /*
1726 Local Variables:
1727 mode:c++
1728 c-file-style:"stroustrup"
1729 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1730 indent-tabs-mode:nil
1731 fill-column:99
1732 End:
1733 */
1734 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :