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