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-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 /** Note that lifetime is measured in milliseconds
398 * it is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
399 * The return value should only be used as argument for SPDesktop::remove_temporary_canvasitem, because the object might be deleted already.
400 */
401 Inkscape::Display::TemporaryItem *
402 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime)
403 {
404 return temporary_item_list->add_item(item, lifetime);
405 }
407 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
408 */
409 void
410 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
411 {
412 // check for non-null temporary_item_list, because during destruction of desktop, some destructor might try to access this list!
413 if (tempitem && temporary_item_list) {
414 temporary_item_list->delete_item(tempitem);
415 }
416 }
418 void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) {
419 SP_CANVAS_ARENA (drawing)->arena->rendermode = mode;
420 canvas->rendermode = mode;
421 _display_mode = mode;
422 if (mode != Inkscape::RENDERMODE_OUTLINE) {
423 _saved_display_mode = _display_mode;
424 }
425 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
426 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
427 }
429 void SPDesktop::displayModeToggle() {
430 if (_display_mode == Inkscape::RENDERMODE_OUTLINE) {
431 _setDisplayMode(_saved_display_mode);
432 } else {
433 _setDisplayMode(Inkscape::RENDERMODE_OUTLINE);
434 }
435 }
437 /**
438 * Returns current root (=bottom) layer.
439 */
440 SPObject *SPDesktop::currentRoot() const
441 {
442 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
443 }
445 /**
446 * Returns current top layer.
447 */
448 SPObject *SPDesktop::currentLayer() const
449 {
450 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
451 }
453 /**
454 * Sets the current layer of the desktop.
455 *
456 * Make \a object the top layer.
457 */
458 void SPDesktop::setCurrentLayer(SPObject *object) {
459 g_return_if_fail(SP_IS_GROUP(object));
460 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
461 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
462 _layer_hierarchy->setBottom(object);
463 }
465 /**
466 * Return layer that contains \a object.
467 */
468 SPObject *SPDesktop::layerForObject(SPObject *object) {
469 g_return_val_if_fail(object != NULL, NULL);
471 SPObject *root=currentRoot();
472 object = SP_OBJECT_PARENT(object);
473 while ( object && object != root && !isLayer(object) ) {
474 object = SP_OBJECT_PARENT(object);
475 }
476 return object;
477 }
479 /**
480 * True if object is a layer.
481 */
482 bool SPDesktop::isLayer(SPObject *object) const {
483 return ( SP_IS_GROUP(object)
484 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
485 == SPGroup::LAYER ) );
486 }
488 /**
489 * True if desktop viewport fully contains \a item's bbox.
490 */
491 bool SPDesktop::isWithinViewport (SPItem *item) const
492 {
493 NR::Rect const viewport = get_display_area();
494 NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
495 if (bbox) {
496 return viewport.contains(*bbox);
497 } else {
498 return true;
499 }
500 }
502 ///
503 bool SPDesktop::itemIsHidden(SPItem const *item) const {
504 return item->isHidden(this->dkey);
505 }
507 /**
508 * Set activate property of desktop; emit signal if changed.
509 */
510 void
511 SPDesktop::set_active (bool new_active)
512 {
513 if (new_active != _active) {
514 _active = new_active;
515 if (new_active) {
516 _activate_signal.emit();
517 } else {
518 _deactivate_signal.emit();
519 }
520 }
521 }
523 /**
524 * Set activate status of current desktop's named view.
525 */
526 void
527 SPDesktop::activate_guides(bool activate)
528 {
529 guides_active = activate;
530 namedview->activateGuides(this, activate);
531 }
533 /**
534 * Make desktop switch documents.
535 */
536 void
537 SPDesktop::change_document (SPDocument *theDocument)
538 {
539 g_return_if_fail (theDocument != NULL);
541 /* unselect everything before switching documents */
542 selection->clear();
544 setDocument (theDocument);
546 /* update the rulers, connect the desktop widget's signal to the new namedview etc.
547 (this can probably be done in a better way) */
548 Gtk::Window *parent = this->getToplevel();
549 g_assert(parent != NULL);
550 SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
551 if (dtw) dtw->desktop = this;
552 sp_desktop_widget_update_namedview(dtw);
554 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
555 _document_replaced_signal.emit (this, theDocument);
556 }
558 /**
559 * Make desktop switch event contexts.
560 */
561 void
562 SPDesktop::set_event_context (GtkType type, const gchar *config)
563 {
564 SPEventContext *ec;
565 while (event_context) {
566 ec = event_context;
567 sp_event_context_deactivate (ec);
568 event_context = ec->next;
569 sp_event_context_finish (ec);
570 g_object_unref (G_OBJECT (ec));
571 }
573 Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
574 ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
575 ec->next = event_context;
576 event_context = ec;
577 sp_event_context_activate (ec);
578 _event_context_changed_signal.emit (this, ec);
579 }
581 /**
582 * Push event context onto desktop's context stack.
583 */
584 void
585 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
586 {
587 SPEventContext *ref, *ec;
588 Inkscape::XML::Node *repr;
590 if (event_context && event_context->key == key) return;
591 ref = event_context;
592 while (ref && ref->next && ref->next->key != key) ref = ref->next;
593 if (ref && ref->next) {
594 ec = ref->next;
595 ref->next = ec->next;
596 sp_event_context_finish (ec);
597 g_object_unref (G_OBJECT (ec));
598 }
600 if (event_context) sp_event_context_deactivate (event_context);
601 repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
602 ec = sp_event_context_new (type, this, repr, key);
603 ec->next = event_context;
604 event_context = ec;
605 sp_event_context_activate (ec);
606 _event_context_changed_signal.emit (this, ec);
607 }
609 /**
610 * Sets the coordinate status to a given point
611 */
612 void
613 SPDesktop::set_coordinate_status (NR::Point p) {
614 _widget->setCoordinateStatus(p);
615 }
617 /**
618 * \see sp_document_item_from_list_at_point_bottom()
619 */
620 SPItem *
621 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
622 {
623 g_return_val_if_fail (doc() != NULL, NULL);
624 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
625 }
627 /**
628 * \see sp_document_item_at_point()
629 */
630 SPItem *
631 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
632 {
633 g_return_val_if_fail (doc() != NULL, NULL);
634 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
635 }
637 /**
638 * \see sp_document_group_at_point()
639 */
640 SPItem *
641 SPDesktop::group_at_point (NR::Point const p) const
642 {
643 g_return_val_if_fail (doc() != NULL, NULL);
644 return sp_document_group_at_point (doc(), dkey, p);
645 }
647 /**
648 * \brief Returns the mouse point in document coordinates; if mouse is
649 * outside the canvas, returns the center of canvas viewpoint
650 */
651 NR::Point
652 SPDesktop::point() const
653 {
654 NR::Point p = _widget->getPointer();
655 NR::Point pw = sp_canvas_window_to_world (canvas, p);
656 p = w2d(pw);
658 NR::Rect const r = canvas->getViewbox();
660 NR::Point r0 = w2d(r.min());
661 NR::Point r1 = w2d(r.max());
663 if (p[NR::X] >= r0[NR::X] &&
664 p[NR::X] <= r1[NR::X] &&
665 p[NR::Y] >= r1[NR::Y] &&
666 p[NR::Y] <= r0[NR::Y])
667 {
668 return p;
669 } else {
670 return (r0 + r1) / 2;
671 }
672 }
674 /**
675 * Put current zoom data in history list.
676 */
677 void
678 SPDesktop::push_current_zoom (GList **history)
679 {
680 NR::Rect const area = get_display_area();
682 NRRect *old_zoom = g_new(NRRect, 1);
683 old_zoom->x0 = area.min()[NR::X];
684 old_zoom->x1 = area.max()[NR::X];
685 old_zoom->y0 = area.min()[NR::Y];
686 old_zoom->y1 = area.max()[NR::Y];
687 if ( *history == NULL
688 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
689 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
690 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
691 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
692 {
693 *history = g_list_prepend (*history, old_zoom);
694 }
695 }
697 /**
698 * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
699 */
700 void
701 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
702 {
703 g_assert(_widget);
705 // save the zoom
706 if (log) {
707 push_current_zoom(&zooms_past);
708 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
709 g_list_free (zooms_future);
710 zooms_future = NULL;
711 }
713 double const cx = 0.5 * (x0 + x1);
714 double const cy = 0.5 * (y0 + y1);
716 NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
718 double scale = expansion(_d2w);
719 double newscale;
720 if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
721 newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
722 } else {
723 newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
724 }
726 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
728 int clear = FALSE;
729 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
730 /* Set zoom factors */
731 _d2w = NR::Matrix(NR::scale(newscale, -newscale));
732 _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
733 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
734 clear = TRUE;
735 }
737 /* Calculate top left corner (in document pixels) */
738 x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
739 y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
741 /* Scroll */
742 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
744 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
745 sp_box3d_context_update_lines(event_context);
747 _widget->updateRulers();
748 _widget->updateScrollbars(expansion(_d2w));
749 _widget->updateZoom();
750 }
752 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
753 {
754 set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
755 }
757 /**
758 * Return viewbox dimensions.
759 */
760 NR::Rect SPDesktop::get_display_area() const
761 {
762 NR::Rect const viewbox = canvas->getViewbox();
764 double const scale = _d2w[0];
766 return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
767 NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
768 }
770 /**
771 * Revert back to previous zoom if possible.
772 */
773 void
774 SPDesktop::prev_zoom()
775 {
776 if (zooms_past == NULL) {
777 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
778 return;
779 }
781 // push current zoom into forward zooms list
782 push_current_zoom (&zooms_future);
784 // restore previous zoom
785 set_display_area (((NRRect *) zooms_past->data)->x0,
786 ((NRRect *) zooms_past->data)->y0,
787 ((NRRect *) zooms_past->data)->x1,
788 ((NRRect *) zooms_past->data)->y1,
789 0, false);
791 // remove the just-added zoom from the past zooms list
792 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
793 }
795 /**
796 * Set zoom to next in list.
797 */
798 void
799 SPDesktop::next_zoom()
800 {
801 if (zooms_future == NULL) {
802 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
803 return;
804 }
806 // push current zoom into past zooms list
807 push_current_zoom (&zooms_past);
809 // restore next zoom
810 set_display_area (((NRRect *) zooms_future->data)->x0,
811 ((NRRect *) zooms_future->data)->y0,
812 ((NRRect *) zooms_future->data)->x1,
813 ((NRRect *) zooms_future->data)->y1,
814 0, false);
816 // remove the just-used zoom from the zooms_future list
817 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
818 }
820 /**
821 * Zoom to point with absolute zoom factor.
822 */
823 void
824 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
825 {
826 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
828 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
829 // this check prevents "sliding" when trying to zoom in at maximum zoom;
830 /// \todo someone please fix calculations properly and remove this hack
831 if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
832 return;
834 NR::Rect const viewbox = canvas->getViewbox();
836 double const width2 = viewbox.dimensions()[NR::X] / zoom;
837 double const height2 = viewbox.dimensions()[NR::Y] / zoom;
839 set_display_area(cx - px * width2,
840 cy - py * height2,
841 cx + (1 - px) * width2,
842 cy + (1 - py) * height2,
843 0.0);
844 }
846 /**
847 * Zoom to center with absolute zoom factor.
848 */
849 void
850 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
851 {
852 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
853 }
855 /**
856 * Zoom to point with relative zoom factor.
857 */
858 void
859 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
860 {
861 NR::Rect const area = get_display_area();
863 if (cx < area.min()[NR::X]) {
864 cx = area.min()[NR::X];
865 }
866 if (cx > area.max()[NR::X]) {
867 cx = area.max()[NR::X];
868 }
869 if (cy < area.min()[NR::Y]) {
870 cy = area.min()[NR::Y];
871 }
872 if (cy > area.max()[NR::Y]) {
873 cy = area.max()[NR::Y];
874 }
876 gdouble const scale = expansion(_d2w) * zoom;
877 double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
878 double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
880 zoom_absolute_keep_point(cx, cy, px, py, scale);
881 }
883 /**
884 * Zoom to center with relative zoom factor.
885 */
886 void
887 SPDesktop::zoom_relative (double cx, double cy, double zoom)
888 {
889 gdouble scale = expansion(_d2w) * zoom;
890 zoom_absolute (cx, cy, scale);
891 }
893 /**
894 * Set display area to origin and current document dimensions.
895 */
896 void
897 SPDesktop::zoom_page()
898 {
899 NR::Rect d(NR::Point(0, 0),
900 NR::Point(sp_document_width(doc()), sp_document_height(doc())));
902 if (d.isEmpty(1.0)) {
903 return;
904 }
906 set_display_area(d, 10);
907 }
909 /**
910 * Set display area to current document width.
911 */
912 void
913 SPDesktop::zoom_page_width()
914 {
915 NR::Rect const a = get_display_area();
917 if (sp_document_width(doc()) < 1.0) {
918 return;
919 }
921 NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
922 NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
924 set_display_area(d, 10);
925 }
927 /**
928 * Zoom to selection.
929 */
930 void
931 SPDesktop::zoom_selection()
932 {
933 NR::Maybe<NR::Rect> const d = selection->bounds();
935 if ( !d || d->isEmpty(0.1) ) {
936 return;
937 }
939 set_display_area(*d, 10);
940 }
942 /**
943 * Tell widget to let zoom widget grab keyboard focus.
944 */
945 void
946 SPDesktop::zoom_grab_focus()
947 {
948 _widget->letZoomGrabFocus();
949 }
951 /**
952 * Zoom to whole drawing.
953 */
954 void
955 SPDesktop::zoom_drawing()
956 {
957 g_return_if_fail (doc() != NULL);
958 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
959 g_return_if_fail (docitem != NULL);
961 NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
963 /* Note that the second condition here indicates that
964 ** there are no items in the drawing.
965 */
966 if ( !d || d->isEmpty(1.0) ) {
967 return;
968 }
970 set_display_area(*d, 10);
971 }
973 /**
974 * Scroll canvas by specific coordinate amount.
975 */
976 void
977 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
978 {
979 g_assert(_widget);
981 NR::Rect const viewbox = canvas->getViewbox();
983 sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
985 /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */
986 sp_box3d_context_update_lines(event_context);
988 _widget->updateRulers();
989 _widget->updateScrollbars(expansion(_d2w));
990 }
992 bool
993 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
994 {
995 gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
997 // autoscrolldistance is in screen pixels, but the display area is in document units
998 autoscrolldistance /= expansion(_d2w);
999 NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
1001 if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
1002 !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y]) ) {
1004 NR::Point const s_w( (*p) * _d2w );
1006 gdouble x_to;
1007 if ((*p)[NR::X] < dbox.min()[NR::X])
1008 x_to = dbox.min()[NR::X];
1009 else if ((*p)[NR::X] > dbox.max()[NR::X])
1010 x_to = dbox.max()[NR::X];
1011 else
1012 x_to = (*p)[NR::X];
1014 gdouble y_to;
1015 if ((*p)[NR::Y] < dbox.min()[NR::Y])
1016 y_to = dbox.min()[NR::Y];
1017 else if ((*p)[NR::Y] > dbox.max()[NR::Y])
1018 y_to = dbox.max()[NR::Y];
1019 else
1020 y_to = (*p)[NR::Y];
1022 NR::Point const d_dt(x_to, y_to);
1023 NR::Point const d_w( d_dt * _d2w );
1024 NR::Point const moved_w( d_w - s_w );
1026 if (autoscrollspeed == 0)
1027 autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
1029 if (autoscrollspeed != 0)
1030 scroll_world (autoscrollspeed * moved_w);
1032 return true;
1033 }
1034 return false;
1035 }
1037 bool
1038 SPDesktop::is_iconified()
1039 {
1040 return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1041 }
1043 void
1044 SPDesktop::iconify()
1045 {
1046 _widget->setIconified();
1047 }
1049 bool
1050 SPDesktop::is_maximized()
1051 {
1052 return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1053 }
1055 void
1056 SPDesktop::maximize()
1057 {
1058 _widget->setMaximized();
1059 }
1061 bool
1062 SPDesktop::is_fullscreen()
1063 {
1064 return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1065 }
1067 void
1068 SPDesktop::fullscreen()
1069 {
1070 _widget->setFullscreen();
1071 }
1073 void
1074 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1075 {
1076 _widget->getGeometry (x, y, w, h);
1077 }
1079 void
1080 SPDesktop::setWindowPosition (NR::Point p)
1081 {
1082 _widget->setPosition (p);
1083 }
1085 void
1086 SPDesktop::setWindowSize (gint w, gint h)
1087 {
1088 _widget->setSize (w, h);
1089 }
1091 void
1092 SPDesktop::setWindowTransient (void *p, int transient_policy)
1093 {
1094 _widget->setTransient (p, transient_policy);
1095 }
1097 Gtk::Window*
1098 SPDesktop::getToplevel( )
1099 {
1100 return _widget->getWindow();
1101 }
1103 void
1104 SPDesktop::presentWindow()
1105 {
1106 _widget->present();
1107 }
1109 bool
1110 SPDesktop::warnDialog (gchar *text)
1111 {
1112 return _widget->warnDialog (text);
1113 }
1115 void
1116 SPDesktop::toggleRulers()
1117 {
1118 _widget->toggleRulers();
1119 }
1121 void
1122 SPDesktop::toggleScrollbars()
1123 {
1124 _widget->toggleScrollbars();
1125 }
1127 void
1128 SPDesktop::layoutWidget()
1129 {
1130 _widget->layout();
1131 }
1133 void
1134 SPDesktop::destroyWidget()
1135 {
1136 _widget->destroy();
1137 }
1139 bool
1140 SPDesktop::shutdown()
1141 {
1142 return _widget->shutdown();
1143 }
1145 bool SPDesktop::onDeleteUI (GdkEventAny*)
1146 {
1147 if(shutdown())
1148 return true;
1150 destroyWidget();
1151 return false;
1152 }
1154 /**
1155 * onWindowStateEvent
1156 *
1157 * Called when the window changes its maximize/fullscreen/iconify/pinned state.
1158 * Since GTK doesn't have a way to query this state information directly, we
1159 * record it for the desktop here, and also possibly trigger a layout.
1160 */
1161 bool
1162 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1163 {
1164 // Record the desktop window's state
1165 window_state = event->new_window_state;
1167 // Layout may differ depending on full-screen mode or not
1168 GdkWindowState changed = event->changed_mask;
1169 if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1170 layoutWidget();
1171 }
1173 return false;
1174 }
1176 void
1177 SPDesktop::setToolboxFocusTo (gchar const *label)
1178 {
1179 _widget->setToolboxFocusTo (label);
1180 }
1182 void
1183 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1184 {
1185 _widget->setToolboxAdjustmentValue (id, val);
1186 }
1188 void
1189 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1190 {
1191 _widget->setToolboxSelectOneValue (id, val);
1192 }
1194 bool
1195 SPDesktop::isToolboxButtonActive (gchar const *id)
1196 {
1197 return _widget->isToolboxButtonActive (id);
1198 }
1200 void
1201 SPDesktop::emitToolSubselectionChanged(gpointer data)
1202 {
1203 _tool_subselection_changed.emit(data);
1204 inkscape_subselection_changed (this);
1205 }
1207 void
1208 SPDesktop::updateNow()
1209 {
1210 sp_canvas_update_now(canvas);
1211 }
1213 void
1214 SPDesktop::enableInteraction()
1215 {
1216 _widget->enableInteraction();
1217 }
1219 void SPDesktop::disableInteraction()
1220 {
1221 _widget->disableInteraction();
1222 }
1224 void SPDesktop::setWaitingCursor()
1225 {
1226 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1227 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1228 gdk_cursor_unref(waiting);
1229 waiting_cursor = true;
1231 // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1232 // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1233 // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1234 // after the call to setWaitingCursor as it was before
1235 while( Gtk::Main::events_pending() )
1236 Gtk::Main::iteration();
1237 }
1239 void SPDesktop::clearWaitingCursor()
1240 {
1241 if (waiting_cursor)
1242 sp_event_context_update_cursor(sp_desktop_event_context(this));
1243 }
1245 void SPDesktop::toggleColorProfAdjust()
1246 {
1247 _widget->toggleColorProfAdjust();
1248 }
1250 void SPDesktop::toggleGrids()
1251 {
1252 if (namedview->grids) {
1253 if(gridgroup) {
1254 showGrids(!grids_visible);
1255 }
1256 } else {
1257 //there is no grid present at the moment. add a rectangular grid and make it visible
1258 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1259 Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1260 showGrids(true);
1261 }
1262 }
1264 void SPDesktop::showGrids(bool show, bool dirty_document)
1265 {
1266 grids_visible = show;
1267 sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1268 if (show) {
1269 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1270 } else {
1271 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1272 }
1273 }
1275 void SPDesktop::toggleSnapping()
1276 {
1277 bool v = namedview->snap_manager.getSnapEnabledGlobally();
1278 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1279 sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1280 }
1282 //----------------------------------------------------------------------
1283 // Callback implementations. The virtual ones are connected by the view.
1285 void
1286 SPDesktop::onPositionSet (double x, double y)
1287 {
1288 _widget->viewSetPosition (NR::Point(x,y));
1289 }
1291 void
1292 SPDesktop::onResized (double /*x*/, double /*y*/)
1293 {
1294 // Nothing called here
1295 }
1297 /**
1298 * Redraw callback; queues Gtk redraw; connected by View.
1299 */
1300 void
1301 SPDesktop::onRedrawRequested ()
1302 {
1303 if (main) {
1304 _widget->requestCanvasUpdate();
1305 }
1306 }
1308 void
1309 SPDesktop::updateCanvasNow()
1310 {
1311 _widget->requestCanvasUpdateAndWait();
1312 }
1314 /**
1315 * Associate document with desktop.
1316 */
1317 void
1318 SPDesktop::setDocument (SPDocument *doc)
1319 {
1320 if (this->doc() && doc) {
1321 namedview->hide(this);
1322 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1323 }
1325 if (_layer_hierarchy) {
1326 _layer_hierarchy->clear();
1327 delete _layer_hierarchy;
1328 }
1329 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1330 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1331 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1332 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1333 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1335 /* setup EventLog */
1336 event_log = new Inkscape::EventLog(doc);
1337 doc->addUndoObserver(*event_log);
1339 _commit_connection.disconnect();
1340 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1342 /// \todo fixme: This condition exists to make sure the code
1343 /// inside is NOT called on initialization, only on replacement. But there
1344 /// are surely more safe methods to accomplish this.
1345 // TODO since the comment had reversed logic, check the intent of this block of code:
1346 if (drawing) {
1347 NRArenaItem *ai = 0;
1349 namedview = sp_document_namedview (doc, NULL);
1350 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1351 number = namedview->getViewCount();
1353 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1354 SP_CANVAS_ARENA (drawing)->arena,
1355 dkey,
1356 SP_ITEM_SHOW_DISPLAY);
1357 if (ai) {
1358 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1359 nr_arena_item_unref (ai);
1360 }
1361 namedview->show(this);
1362 /* Ugly hack */
1363 activate_guides (true);
1364 /* Ugly hack */
1365 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1366 }
1368 _document_replaced_signal.emit (this, doc);
1370 View::setDocument (doc);
1371 }
1373 void
1374 SPDesktop::onStatusMessage
1375 (Inkscape::MessageType type, gchar const *message)
1376 {
1377 if (_widget) {
1378 _widget->setMessage(type, message);
1379 }
1380 }
1382 void
1383 SPDesktop::onDocumentURISet (gchar const* uri)
1384 {
1385 _widget->setTitle(uri);
1386 }
1388 /**
1389 * Resized callback.
1390 */
1391 void
1392 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1393 {
1394 _doc2dt[5] = height;
1395 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1396 NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1397 SP_CTRLRECT(page)->setRectangle(a);
1398 SP_CTRLRECT(page_border)->setRectangle(a);
1399 }
1402 void
1403 SPDesktop::_onActivate (SPDesktop* dt)
1404 {
1405 if (!dt->_widget) return;
1406 dt->_widget->activateDesktop();
1407 }
1409 void
1410 SPDesktop::_onDeactivate (SPDesktop* dt)
1411 {
1412 if (!dt->_widget) return;
1413 dt->_widget->deactivateDesktop();
1414 }
1416 void
1417 SPDesktop::_onSelectionModified
1418 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1419 {
1420 if (!dt->_widget) return;
1421 dt->_widget->updateScrollbars (expansion(dt->_d2w));
1422 }
1424 static void
1425 _onSelectionChanged
1426 (Inkscape::Selection *selection, SPDesktop *desktop)
1427 {
1428 /** \todo
1429 * only change the layer for single selections, or what?
1430 * This seems reasonable -- for multiple selections there can be many
1431 * different layers involved.
1432 */
1433 SPItem *item=selection->singleItem();
1434 if (item) {
1435 SPObject *layer=desktop->layerForObject(item);
1436 if ( layer && layer != desktop->currentLayer() ) {
1437 desktop->setCurrentLayer(layer);
1438 }
1439 }
1440 }
1442 /**
1443 * Calls event handler of current event context.
1444 * \param arena Unused
1445 * \todo fixme
1446 */
1447 static gint
1448 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1449 {
1450 if (ai) {
1451 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1452 return sp_event_context_item_handler (desktop->event_context, spi, event);
1453 } else {
1454 return sp_event_context_root_handler (desktop->event_context, event);
1455 }
1456 }
1458 static void
1459 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1460 g_return_if_fail(SP_IS_GROUP(layer));
1461 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1462 }
1464 /// Callback
1465 static void
1466 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1467 g_return_if_fail(SP_IS_GROUP(layer));
1468 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1469 }
1471 /// Callback
1472 static void
1473 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1474 SPDesktop *desktop)
1475 {
1476 desktop->_layer_changed_signal.emit (bottom);
1477 }
1479 /// Called when document is starting to be rebuilt.
1480 static void
1481 _reconstruction_start (SPDesktop * desktop)
1482 {
1483 // printf("Desktop, starting reconstruction\n");
1484 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1485 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1487 /*
1488 GSList const * selection_objs = desktop->selection->list();
1489 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1491 }
1492 */
1493 desktop->selection->clear();
1495 // printf("Desktop, starting reconstruction end\n");
1496 }
1498 /// Called when document rebuild is finished.
1499 static void
1500 _reconstruction_finish (SPDesktop * desktop)
1501 {
1502 // printf("Desktop, finishing reconstruction\n");
1503 if (desktop->_reconstruction_old_layer_id == NULL)
1504 return;
1506 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1507 if (newLayer != NULL)
1508 desktop->setCurrentLayer(newLayer);
1510 g_free(desktop->_reconstruction_old_layer_id);
1511 desktop->_reconstruction_old_layer_id = NULL;
1512 // printf("Desktop, finishing reconstruction end\n");
1513 return;
1514 }
1516 /**
1517 * Namedview_modified callback.
1518 */
1519 static void
1520 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1521 {
1522 SPNamedView *nv=SP_NAMEDVIEW(obj);
1524 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1526 /* Recalculate snap distances */
1527 /* FIXME: why is the desktop getting involved in setting up something
1528 ** that is entirely to do with the namedview?
1529 */
1530 _update_snap_distances (desktop);
1532 /* Show/hide page background */
1533 if (nv->pagecolor & 0xff) {
1534 sp_canvas_item_show (desktop->table);
1535 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1536 sp_canvas_item_move_to_z (desktop->table, 0);
1537 } else {
1538 sp_canvas_item_hide (desktop->table);
1539 }
1541 /* Show/hide page border */
1542 if (nv->showborder) {
1543 // show
1544 sp_canvas_item_show (desktop->page_border);
1545 // set color and shadow
1546 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1547 if (nv->pageshadow) {
1548 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1549 }
1550 // place in the z-order stack
1551 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1552 sp_canvas_item_move_to_z (desktop->page_border, 2);
1553 } else {
1554 int order = sp_canvas_item_order (desktop->page_border);
1555 int morder = sp_canvas_item_order (desktop->drawing);
1556 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1557 morder - order);
1558 }
1559 } else {
1560 sp_canvas_item_hide (desktop->page_border);
1561 if (nv->pageshadow) {
1562 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1563 }
1564 }
1566 /* Show/hide page shadow */
1567 if (nv->showpageshadow && nv->pageshadow) {
1568 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1569 } else {
1570 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1571 }
1573 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1574 (SP_RGBA32_R_U(nv->pagecolor) +
1575 SP_RGBA32_G_U(nv->pagecolor) +
1576 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1577 // the background color is light or transparent, use black outline
1578 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1579 } else { // use white outline
1580 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1581 }
1582 }
1583 }
1585 /**
1586 * Callback to reset snapper's distances.
1587 */
1588 static void
1589 _update_snap_distances (SPDesktop *desktop)
1590 {
1591 SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1593 SPNamedView &nv = *desktop->namedview;
1595 //tell all grid snappers
1596 for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1597 Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1598 grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
1599 *nv.gridtoleranceunit,
1600 px));
1601 }
1603 nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
1604 *nv.guidetoleranceunit,
1605 px));
1606 nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
1607 *nv.objecttoleranceunit,
1608 px));
1609 }
1612 NR::Matrix SPDesktop::w2d() const
1613 {
1614 return _w2d;
1615 }
1617 NR::Point SPDesktop::w2d(NR::Point const &p) const
1618 {
1619 return p * _w2d;
1620 }
1622 NR::Point SPDesktop::d2w(NR::Point const &p) const
1623 {
1624 return p * _d2w;
1625 }
1627 NR::Matrix SPDesktop::doc2dt() const
1628 {
1629 return _doc2dt;
1630 }
1632 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1633 {
1634 return p * _doc2dt;
1635 }
1637 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1638 {
1639 return p / _doc2dt;
1640 }
1643 /**
1644 * Pop event context from desktop's context stack. Never used.
1645 */
1646 // void
1647 // SPDesktop::pop_event_context (unsigned int key)
1648 // {
1649 // SPEventContext *ec = NULL;
1650 //
1651 // if (event_context && event_context->key == key) {
1652 // g_return_if_fail (event_context);
1653 // g_return_if_fail (event_context->next);
1654 // ec = event_context;
1655 // sp_event_context_deactivate (ec);
1656 // event_context = ec->next;
1657 // sp_event_context_activate (event_context);
1658 // _event_context_changed_signal.emit (this, ec);
1659 // }
1660 //
1661 // SPEventContext *ref = event_context;
1662 // while (ref && ref->next && ref->next->key != key)
1663 // ref = ref->next;
1664 //
1665 // if (ref && ref->next) {
1666 // ec = ref->next;
1667 // ref->next = ec->next;
1668 // }
1669 //
1670 // if (ec) {
1671 // sp_event_context_finish (ec);
1672 // g_object_unref (G_OBJECT (ec));
1673 // }
1674 // }
1676 /*
1677 Local Variables:
1678 mode:c++
1679 c-file-style:"stroustrup"
1680 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1681 indent-tabs-mode:nil
1682 fill-column:99
1683 End:
1684 */
1685 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :