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 "libnr/nr-matrix-div.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 "layer-manager.h"
87 #include "event-log.h"
88 #include "display/canvas-grid.h"
89 #include "widgets/desktop-widget.h"
91 #include "display/sp-canvas.h"
93 namespace Inkscape { namespace XML { class Node; }}
95 // Callback declarations
96 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
97 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
98 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
99 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
100 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
101 static void _reconstruction_start(SPDesktop * desktop);
102 static void _reconstruction_finish(SPDesktop * desktop);
103 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
104 static void _update_snap_distances (SPDesktop *desktop);
106 /**
107 * Return new desktop object.
108 * \pre namedview != NULL.
109 * \pre canvas != NULL.
110 */
111 SPDesktop::SPDesktop() :
112 _dlg_mgr( 0 ),
113 namedview( 0 ),
114 canvas( 0 ),
115 selection( 0 ),
116 event_context( 0 ),
117 layer_manager( 0 ),
118 event_log( 0 ),
119 temporary_item_list( 0 ),
120 acetate( 0 ),
121 main( 0 ),
122 gridgroup( 0 ),
123 guides( 0 ),
124 drawing( 0 ),
125 sketch( 0 ),
126 controls( 0 ),
127 tempgroup ( 0 ),
128 table( 0 ),
129 page( 0 ),
130 page_border( 0 ),
131 current( 0 ),
132 zooms_past( 0 ),
133 zooms_future( 0 ),
134 dkey( 0 ),
135 number( 0 ),
136 window_state(0),
137 interaction_disabled_counter( 0 ),
138 waiting_cursor( false ),
139 guides_active( false ),
140 gr_item( 0 ),
141 gr_point_type( 0 ),
142 gr_point_i( 0 ),
143 gr_fill_or_stroke( true ),
144 _layer_hierarchy( 0 ),
145 _reconstruction_old_layer_id( 0 ),
146 _widget( 0 ),
147 _inkscape( 0 ),
148 _guides_message_context( 0 ),
149 _active( false ),
150 _w2d(),
151 _d2w(),
152 _doc2dt( NR::Matrix(NR::scale(1, -1)) ),
153 grids_visible( false )
154 {
155 _d2w.set_identity();
156 _w2d.set_identity();
158 selection = Inkscape::GC::release( new Inkscape::Selection(this) );
159 }
161 void
162 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
163 {
164 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
166 current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
168 namedview = nv;
169 canvas = aCanvas;
171 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
172 /* Kill flicker */
173 sp_document_ensure_up_to_date (document);
175 /* Setup Dialog Manager */
176 _dlg_mgr = &Inkscape::UI::Dialog::DialogManager::getInstance();
178 dkey = sp_item_display_key_new (1);
180 /* Connect document */
181 setDocument (document);
183 number = namedview->getViewCount();
186 /* Setup Canvas */
187 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
189 SPCanvasGroup *root = sp_canvas_root (canvas);
191 /* Setup adminstrative layers */
192 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
193 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
194 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
195 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
197 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
198 SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
199 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
200 sp_canvas_item_move_to_z (table, 0);
202 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
203 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
204 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
206 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
207 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
209 SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
211 if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
212 // Start in outline mode
213 setDisplayModeOutline();
214 } else {
215 // Start in normal mode, default
216 setDisplayModeNormal();
217 }
219 gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
220 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
221 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
222 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
223 tempgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
225 /* Push select tool to the bottom of stack */
226 /** \todo
227 * FIXME: this is the only call to this. Everything else seems to just
228 * call "set" instead of "push". Can we assume that there is only one
229 * context ever?
230 */
231 push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
233 // display rect and zoom are now handled in sp_desktop_widget_realize()
235 NR::Rect const d(NR::Point(0.0, 0.0),
236 NR::Point(sp_document_width(document), sp_document_height(document)));
238 SP_CTRLRECT(page)->setRectangle(d);
239 SP_CTRLRECT(page_border)->setRectangle(d);
241 /* the following sets the page shadow on the canvas
242 It was originally set to 5, which is really cheesy!
243 It now is an attribute in the document's namedview. If a value of
244 0 is used, then the constructor for a shadow is not initialized.
245 */
247 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
248 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
249 }
252 /* Connect event for page resize */
253 _doc2dt[5] = sp_document_height (document);
254 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
256 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
258 NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
259 SP_CANVAS_ARENA (drawing)->arena,
260 dkey,
261 SP_ITEM_SHOW_DISPLAY);
262 if (ai) {
263 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
264 nr_arena_item_unref (ai);
265 }
267 namedview->show(this);
268 /* Ugly hack */
269 activate_guides (true);
270 /* Ugly hack */
271 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
273 /* Set up notification of rebuilding the document, this allows
274 for saving object related settings in the document. */
275 _reconstruction_start_connection =
276 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
277 _reconstruction_finish_connection =
278 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
279 _reconstruction_old_layer_id = NULL;
281 // ?
282 // sp_active_desktop_set (desktop);
283 _inkscape = INKSCAPE;
285 _activate_connection = _activate_signal.connect(
286 sigc::bind(
287 sigc::ptr_fun(_onActivate),
288 this
289 )
290 );
291 _deactivate_connection = _deactivate_signal.connect(
292 sigc::bind(
293 sigc::ptr_fun(_onDeactivate),
294 this
295 )
296 );
298 _sel_modified_connection = selection->connectModified(
299 sigc::bind(
300 sigc::ptr_fun(&_onSelectionModified),
301 this
302 )
303 );
304 _sel_changed_connection = selection->connectChanged(
305 sigc::bind(
306 sigc::ptr_fun(&_onSelectionChanged),
307 this
308 )
309 );
312 /* setup LayerManager */
313 // (Setting up after the connections are all in place, as it may use some of them)
314 layer_manager = new Inkscape::LayerManager( this );
316 showGrids(namedview->grids_visible, false);
318 temporary_item_list = new Inkscape::Display::TemporaryItemList( this );
319 }
322 void SPDesktop::destroy()
323 {
324 delete temporary_item_list;
325 temporary_item_list = NULL;
327 namedview->hide(this);
329 _activate_connection.disconnect();
330 _deactivate_connection.disconnect();
331 _sel_modified_connection.disconnect();
332 _sel_changed_connection.disconnect();
333 _modified_connection.disconnect();
334 _commit_connection.disconnect();
335 _reconstruction_start_connection.disconnect();
336 _reconstruction_finish_connection.disconnect();
338 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
339 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
340 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
342 while (event_context) {
343 SPEventContext *ec = event_context;
344 event_context = ec->next;
345 sp_event_context_finish (ec);
346 g_object_unref (G_OBJECT (ec));
347 }
349 if (_layer_hierarchy) {
350 delete _layer_hierarchy;
351 // _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
352 }
354 if (layer_manager) {
355 delete layer_manager;
356 layer_manager = NULL;
357 }
359 if (_inkscape) {
360 _inkscape = NULL;
361 }
363 if (drawing) {
364 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
365 drawing = NULL;
366 }
368 delete _guides_message_context;
369 _guides_message_context = NULL;
371 g_list_free (zooms_past);
372 g_list_free (zooms_future);
373 }
375 SPDesktop::~SPDesktop() {}
377 //--------------------------------------------------------------------
378 /* Public methods */
381 /** Note that lifetime is measured in milliseconds
382 * it is perfectly safe to ignore the returned pointer: the object is deleted by itself, so don't delete it elsewhere!
383 * The return value should only be used as argument for SPDesktop::remove_temporary_canvasitem, because the object might be deleted already.
384 */
385 Inkscape::Display::TemporaryItem *
386 SPDesktop::add_temporary_canvasitem (SPCanvasItem *item, guint lifetime)
387 {
388 return temporary_item_list->add_item(item, lifetime);
389 }
391 /** It is perfectly safe to call this function while the object has already been deleted due to a timeout.
392 */
393 void
394 SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempitem)
395 {
396 temporary_item_list->delete_item(tempitem);
397 }
399 void SPDesktop::setDisplayModeNormal()
400 {
401 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
402 canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
403 displayMode = RENDERMODE_NORMAL;
404 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
405 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
406 }
408 void SPDesktop::setDisplayModeOutline()
409 {
410 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
411 canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
412 displayMode = RENDERMODE_OUTLINE;
413 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
414 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
415 }
417 void SPDesktop::displayModeToggle()
418 {
419 if (displayMode == RENDERMODE_OUTLINE)
420 setDisplayModeNormal();
421 else
422 setDisplayModeOutline();
423 }
425 /**
426 * Returns current root (=bottom) layer.
427 */
428 SPObject *SPDesktop::currentRoot() const
429 {
430 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
431 }
433 /**
434 * Returns current top layer.
435 */
436 SPObject *SPDesktop::currentLayer() const
437 {
438 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
439 }
441 /**
442 * Sets the current layer of the desktop.
443 *
444 * Make \a object the top layer.
445 */
446 void SPDesktop::setCurrentLayer(SPObject *object) {
447 g_return_if_fail(SP_IS_GROUP(object));
448 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
449 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
450 _layer_hierarchy->setBottom(object);
451 }
453 /**
454 * Return layer that contains \a object.
455 */
456 SPObject *SPDesktop::layerForObject(SPObject *object) {
457 g_return_val_if_fail(object != NULL, NULL);
459 SPObject *root=currentRoot();
460 object = SP_OBJECT_PARENT(object);
461 while ( object && object != root && !isLayer(object) ) {
462 object = SP_OBJECT_PARENT(object);
463 }
464 return object;
465 }
467 /**
468 * True if object is a layer.
469 */
470 bool SPDesktop::isLayer(SPObject *object) const {
471 return ( SP_IS_GROUP(object)
472 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
473 == SPGroup::LAYER ) );
474 }
476 /**
477 * True if desktop viewport fully contains \a item's bbox.
478 */
479 bool SPDesktop::isWithinViewport (SPItem *item) const
480 {
481 NR::Rect const viewport = get_display_area();
482 NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
483 if (bbox) {
484 return viewport.contains(*bbox);
485 } else {
486 return true;
487 }
488 }
490 ///
491 bool SPDesktop::itemIsHidden(SPItem const *item) const {
492 return item->isHidden(this->dkey);
493 }
495 /**
496 * Set activate property of desktop; emit signal if changed.
497 */
498 void
499 SPDesktop::set_active (bool new_active)
500 {
501 if (new_active != _active) {
502 _active = new_active;
503 if (new_active) {
504 _activate_signal.emit();
505 } else {
506 _deactivate_signal.emit();
507 }
508 }
509 }
511 /**
512 * Set activate status of current desktop's named view.
513 */
514 void
515 SPDesktop::activate_guides(bool activate)
516 {
517 guides_active = activate;
518 namedview->activateGuides(this, activate);
519 }
521 /**
522 * Make desktop switch documents.
523 */
524 void
525 SPDesktop::change_document (SPDocument *theDocument)
526 {
527 g_return_if_fail (theDocument != NULL);
529 /* unselect everything before switching documents */
530 selection->clear();
532 setDocument (theDocument);
534 /* update the rulers, connect the desktop widget's signal to the new namedview etc.
535 (this can probably be done in a better way) */
536 Gtk::Window *parent = this->getToplevel();
537 g_assert(parent != NULL);
538 SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget");
539 if (dtw) dtw->desktop = this;
540 sp_desktop_widget_update_namedview(dtw);
542 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
543 _document_replaced_signal.emit (this, theDocument);
544 }
546 /**
547 * Make desktop switch event contexts.
548 */
549 void
550 SPDesktop::set_event_context (GtkType type, const gchar *config)
551 {
552 SPEventContext *ec;
553 while (event_context) {
554 ec = event_context;
555 sp_event_context_deactivate (ec);
556 event_context = ec->next;
557 sp_event_context_finish (ec);
558 g_object_unref (G_OBJECT (ec));
559 }
561 Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
562 ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
563 ec->next = event_context;
564 event_context = ec;
565 sp_event_context_activate (ec);
566 _event_context_changed_signal.emit (this, ec);
567 }
569 /**
570 * Push event context onto desktop's context stack.
571 */
572 void
573 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
574 {
575 SPEventContext *ref, *ec;
576 Inkscape::XML::Node *repr;
578 if (event_context && event_context->key == key) return;
579 ref = event_context;
580 while (ref && ref->next && ref->next->key != key) ref = ref->next;
581 if (ref && ref->next) {
582 ec = ref->next;
583 ref->next = ec->next;
584 sp_event_context_finish (ec);
585 g_object_unref (G_OBJECT (ec));
586 }
588 if (event_context) sp_event_context_deactivate (event_context);
589 repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
590 ec = sp_event_context_new (type, this, repr, key);
591 ec->next = event_context;
592 event_context = ec;
593 sp_event_context_activate (ec);
594 _event_context_changed_signal.emit (this, ec);
595 }
597 /**
598 * Sets the coordinate status to a given point
599 */
600 void
601 SPDesktop::set_coordinate_status (NR::Point p) {
602 _widget->setCoordinateStatus(p);
603 }
605 /**
606 * \see sp_document_item_from_list_at_point_bottom()
607 */
608 SPItem *
609 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
610 {
611 g_return_val_if_fail (doc() != NULL, NULL);
612 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
613 }
615 /**
616 * \see sp_document_item_at_point()
617 */
618 SPItem *
619 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
620 {
621 g_return_val_if_fail (doc() != NULL, NULL);
622 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
623 }
625 /**
626 * \see sp_document_group_at_point()
627 */
628 SPItem *
629 SPDesktop::group_at_point (NR::Point const p) const
630 {
631 g_return_val_if_fail (doc() != NULL, NULL);
632 return sp_document_group_at_point (doc(), dkey, p);
633 }
635 /**
636 * \brief Returns the mouse point in document coordinates; if mouse is
637 * outside the canvas, returns the center of canvas viewpoint
638 */
639 NR::Point
640 SPDesktop::point() const
641 {
642 NR::Point p = _widget->getPointer();
643 NR::Point pw = sp_canvas_window_to_world (canvas, p);
644 p = w2d(pw);
646 NR::Rect const r = canvas->getViewbox();
648 NR::Point r0 = w2d(r.min());
649 NR::Point r1 = w2d(r.max());
651 if (p[NR::X] >= r0[NR::X] &&
652 p[NR::X] <= r1[NR::X] &&
653 p[NR::Y] >= r1[NR::Y] &&
654 p[NR::Y] <= r0[NR::Y])
655 {
656 return p;
657 } else {
658 return (r0 + r1) / 2;
659 }
660 }
662 /**
663 * Put current zoom data in history list.
664 */
665 void
666 SPDesktop::push_current_zoom (GList **history)
667 {
668 NR::Rect const area = get_display_area();
670 NRRect *old_zoom = g_new(NRRect, 1);
671 old_zoom->x0 = area.min()[NR::X];
672 old_zoom->x1 = area.max()[NR::X];
673 old_zoom->y0 = area.min()[NR::Y];
674 old_zoom->y1 = area.max()[NR::Y];
675 if ( *history == NULL
676 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
677 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
678 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
679 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
680 {
681 *history = g_list_prepend (*history, old_zoom);
682 }
683 }
685 /**
686 * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels).
687 */
688 void
689 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
690 {
691 g_assert(_widget);
693 // save the zoom
694 if (log) {
695 push_current_zoom(&zooms_past);
696 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
697 g_list_free (zooms_future);
698 zooms_future = NULL;
699 }
701 double const cx = 0.5 * (x0 + x1);
702 double const cy = 0.5 * (y0 + y1);
704 NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
706 double scale = expansion(_d2w);
707 double newscale;
708 if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
709 newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
710 } else {
711 newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
712 }
714 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels'
716 int clear = FALSE;
717 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
718 /* Set zoom factors */
719 _d2w = NR::Matrix(NR::scale(newscale, -newscale));
720 _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
721 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
722 clear = TRUE;
723 }
725 /* Calculate top left corner (in document pixels) */
726 x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
727 y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
729 /* Scroll */
730 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
732 _widget->updateRulers();
733 _widget->updateScrollbars(expansion(_d2w));
734 _widget->updateZoom();
735 }
737 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
738 {
739 set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
740 }
742 /**
743 * Return viewbox dimensions.
744 */
745 NR::Rect SPDesktop::get_display_area() const
746 {
747 NR::Rect const viewbox = canvas->getViewbox();
749 double const scale = _d2w[0];
751 return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
752 NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
753 }
755 /**
756 * Revert back to previous zoom if possible.
757 */
758 void
759 SPDesktop::prev_zoom()
760 {
761 if (zooms_past == NULL) {
762 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
763 return;
764 }
766 // push current zoom into forward zooms list
767 push_current_zoom (&zooms_future);
769 // restore previous zoom
770 set_display_area (((NRRect *) zooms_past->data)->x0,
771 ((NRRect *) zooms_past->data)->y0,
772 ((NRRect *) zooms_past->data)->x1,
773 ((NRRect *) zooms_past->data)->y1,
774 0, false);
776 // remove the just-added zoom from the past zooms list
777 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
778 }
780 /**
781 * Set zoom to next in list.
782 */
783 void
784 SPDesktop::next_zoom()
785 {
786 if (zooms_future == NULL) {
787 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
788 return;
789 }
791 // push current zoom into past zooms list
792 push_current_zoom (&zooms_past);
794 // restore next zoom
795 set_display_area (((NRRect *) zooms_future->data)->x0,
796 ((NRRect *) zooms_future->data)->y0,
797 ((NRRect *) zooms_future->data)->x1,
798 ((NRRect *) zooms_future->data)->y1,
799 0, false);
801 // remove the just-used zoom from the zooms_future list
802 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
803 }
805 /**
806 * Zoom to point with absolute zoom factor.
807 */
808 void
809 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
810 {
811 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
813 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
814 // this check prevents "sliding" when trying to zoom in at maximum zoom;
815 /// \todo someone please fix calculations properly and remove this hack
816 if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
817 return;
819 NR::Rect const viewbox = canvas->getViewbox();
821 double const width2 = viewbox.dimensions()[NR::X] / zoom;
822 double const height2 = viewbox.dimensions()[NR::Y] / zoom;
824 set_display_area(cx - px * width2,
825 cy - py * height2,
826 cx + (1 - px) * width2,
827 cy + (1 - py) * height2,
828 0.0);
829 }
831 /**
832 * Zoom to center with absolute zoom factor.
833 */
834 void
835 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
836 {
837 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
838 }
840 /**
841 * Zoom to point with relative zoom factor.
842 */
843 void
844 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
845 {
846 NR::Rect const area = get_display_area();
848 if (cx < area.min()[NR::X]) {
849 cx = area.min()[NR::X];
850 }
851 if (cx > area.max()[NR::X]) {
852 cx = area.max()[NR::X];
853 }
854 if (cy < area.min()[NR::Y]) {
855 cy = area.min()[NR::Y];
856 }
857 if (cy > area.max()[NR::Y]) {
858 cy = area.max()[NR::Y];
859 }
861 gdouble const scale = expansion(_d2w) * zoom;
862 double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
863 double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
865 zoom_absolute_keep_point(cx, cy, px, py, scale);
866 }
868 /**
869 * Zoom to center with relative zoom factor.
870 */
871 void
872 SPDesktop::zoom_relative (double cx, double cy, double zoom)
873 {
874 gdouble scale = expansion(_d2w) * zoom;
875 zoom_absolute (cx, cy, scale);
876 }
878 /**
879 * Set display area to origin and current document dimensions.
880 */
881 void
882 SPDesktop::zoom_page()
883 {
884 NR::Rect d(NR::Point(0, 0),
885 NR::Point(sp_document_width(doc()), sp_document_height(doc())));
887 if (d.isEmpty(1.0)) {
888 return;
889 }
891 set_display_area(d, 10);
892 }
894 /**
895 * Set display area to current document width.
896 */
897 void
898 SPDesktop::zoom_page_width()
899 {
900 NR::Rect const a = get_display_area();
902 if (sp_document_width(doc()) < 1.0) {
903 return;
904 }
906 NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
907 NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
909 set_display_area(d, 10);
910 }
912 /**
913 * Zoom to selection.
914 */
915 void
916 SPDesktop::zoom_selection()
917 {
918 NR::Maybe<NR::Rect> const d = selection->bounds();
920 if ( !d || d->isEmpty(0.1) ) {
921 return;
922 }
924 set_display_area(*d, 10);
925 }
927 /**
928 * Tell widget to let zoom widget grab keyboard focus.
929 */
930 void
931 SPDesktop::zoom_grab_focus()
932 {
933 _widget->letZoomGrabFocus();
934 }
936 /**
937 * Zoom to whole drawing.
938 */
939 void
940 SPDesktop::zoom_drawing()
941 {
942 g_return_if_fail (doc() != NULL);
943 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
944 g_return_if_fail (docitem != NULL);
946 NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
948 /* Note that the second condition here indicates that
949 ** there are no items in the drawing.
950 */
951 if ( !d || d->isEmpty(1.0) ) {
952 return;
953 }
955 set_display_area(*d, 10);
956 }
958 /**
959 * Scroll canvas by specific coordinate amount.
960 */
961 void
962 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
963 {
964 g_assert(_widget);
966 NR::Rect const viewbox = canvas->getViewbox();
968 sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
970 _widget->updateRulers();
971 _widget->updateScrollbars(expansion(_d2w));
972 }
974 bool
975 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
976 {
977 gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
979 // autoscrolldistance is in screen pixels, but the display area is in document units
980 autoscrolldistance /= expansion(_d2w);
981 NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
983 if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
984 !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y]) ) {
986 NR::Point const s_w( (*p) * _d2w );
988 gdouble x_to;
989 if ((*p)[NR::X] < dbox.min()[NR::X])
990 x_to = dbox.min()[NR::X];
991 else if ((*p)[NR::X] > dbox.max()[NR::X])
992 x_to = dbox.max()[NR::X];
993 else
994 x_to = (*p)[NR::X];
996 gdouble y_to;
997 if ((*p)[NR::Y] < dbox.min()[NR::Y])
998 y_to = dbox.min()[NR::Y];
999 else if ((*p)[NR::Y] > dbox.max()[NR::Y])
1000 y_to = dbox.max()[NR::Y];
1001 else
1002 y_to = (*p)[NR::Y];
1004 NR::Point const d_dt(x_to, y_to);
1005 NR::Point const d_w( d_dt * _d2w );
1006 NR::Point const moved_w( d_w - s_w );
1008 if (autoscrollspeed == 0)
1009 autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
1011 if (autoscrollspeed != 0)
1012 scroll_world (autoscrollspeed * moved_w);
1014 return true;
1015 }
1016 return false;
1017 }
1019 bool
1020 SPDesktop::is_iconified()
1021 {
1022 return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
1023 }
1025 void
1026 SPDesktop::iconify()
1027 {
1028 _widget->setIconified();
1029 }
1031 bool
1032 SPDesktop::is_maximized()
1033 {
1034 return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
1035 }
1037 void
1038 SPDesktop::maximize()
1039 {
1040 _widget->setMaximized();
1041 }
1043 bool
1044 SPDesktop::is_fullscreen()
1045 {
1046 return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1047 }
1049 void
1050 SPDesktop::fullscreen()
1051 {
1052 _widget->setFullscreen();
1053 }
1055 void
1056 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1057 {
1058 _widget->getGeometry (x, y, w, h);
1059 }
1061 void
1062 SPDesktop::setWindowPosition (NR::Point p)
1063 {
1064 _widget->setPosition (p);
1065 }
1067 void
1068 SPDesktop::setWindowSize (gint w, gint h)
1069 {
1070 _widget->setSize (w, h);
1071 }
1073 void
1074 SPDesktop::setWindowTransient (void *p, int transient_policy)
1075 {
1076 _widget->setTransient (p, transient_policy);
1077 }
1079 Gtk::Window*
1080 SPDesktop::getToplevel( )
1081 {
1082 return _widget->getWindow();
1083 }
1085 void
1086 SPDesktop::presentWindow()
1087 {
1088 _widget->present();
1089 }
1091 bool
1092 SPDesktop::warnDialog (gchar *text)
1093 {
1094 return _widget->warnDialog (text);
1095 }
1097 void
1098 SPDesktop::toggleRulers()
1099 {
1100 _widget->toggleRulers();
1101 }
1103 void
1104 SPDesktop::toggleScrollbars()
1105 {
1106 _widget->toggleScrollbars();
1107 }
1109 void
1110 SPDesktop::layoutWidget()
1111 {
1112 _widget->layout();
1113 }
1115 void
1116 SPDesktop::destroyWidget()
1117 {
1118 _widget->destroy();
1119 }
1121 bool
1122 SPDesktop::shutdown()
1123 {
1124 return _widget->shutdown();
1125 }
1127 bool SPDesktop::onDeleteUI (GdkEventAny*)
1128 {
1129 if(shutdown())
1130 return true;
1132 destroyWidget();
1133 return false;
1134 }
1136 /**
1137 * onWindowStateEvent
1138 *
1139 * Called when the window changes its maximize/fullscreen/iconify/pinned state.
1140 * Since GTK doesn't have a way to query this state information directly, we
1141 * record it for the desktop here, and also possibly trigger a layout.
1142 */
1143 bool
1144 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1145 {
1146 // Record the desktop window's state
1147 window_state = event->new_window_state;
1149 // Layout may differ depending on full-screen mode or not
1150 GdkWindowState changed = event->changed_mask;
1151 if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1152 layoutWidget();
1153 }
1155 return false;
1156 }
1158 void
1159 SPDesktop::setToolboxFocusTo (gchar const *label)
1160 {
1161 _widget->setToolboxFocusTo (label);
1162 }
1164 void
1165 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1166 {
1167 _widget->setToolboxAdjustmentValue (id, val);
1168 }
1170 void
1171 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1172 {
1173 _widget->setToolboxSelectOneValue (id, val);
1174 }
1176 bool
1177 SPDesktop::isToolboxButtonActive (gchar const *id)
1178 {
1179 return _widget->isToolboxButtonActive (id);
1180 }
1182 void
1183 SPDesktop::emitToolSubselectionChanged(gpointer data)
1184 {
1185 _tool_subselection_changed.emit(data);
1186 inkscape_subselection_changed (this);
1187 }
1189 void
1190 SPDesktop::updateNow()
1191 {
1192 sp_canvas_update_now(canvas);
1193 }
1195 void
1196 SPDesktop::enableInteraction()
1197 {
1198 _widget->enableInteraction();
1199 }
1201 void SPDesktop::disableInteraction()
1202 {
1203 _widget->disableInteraction();
1204 }
1206 void SPDesktop::setWaitingCursor()
1207 {
1208 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1209 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1210 gdk_cursor_unref(waiting);
1211 waiting_cursor = true;
1213 // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1214 // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1215 // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1216 // after the call to setWaitingCursor as it was before
1217 while( Gtk::Main::events_pending() )
1218 Gtk::Main::iteration();
1219 }
1221 void SPDesktop::clearWaitingCursor()
1222 {
1223 if (waiting_cursor)
1224 sp_event_context_update_cursor(sp_desktop_event_context(this));
1225 }
1227 void SPDesktop::toggleColorProfAdjust()
1228 {
1229 _widget->toggleColorProfAdjust();
1230 }
1232 void SPDesktop::toggleGrids()
1233 {
1234 if (namedview->grids) {
1235 if(gridgroup) {
1236 showGrids(!grids_visible);
1237 }
1238 } else {
1239 //there is no grid present at the moment. add a rectangular grid and make it visible
1240 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1241 Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1242 showGrids(true);
1243 }
1244 }
1246 void SPDesktop::showGrids(bool show, bool dirty_document)
1247 {
1248 grids_visible = show;
1249 sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1250 if (show) {
1251 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1252 } else {
1253 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1254 }
1255 }
1257 void SPDesktop::toggleSnapping()
1258 {
1259 bool v = namedview->snap_manager.getSnapEnabledGlobally();
1260 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1261 sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1262 }
1264 //----------------------------------------------------------------------
1265 // Callback implementations. The virtual ones are connected by the view.
1267 void
1268 SPDesktop::onPositionSet (double x, double y)
1269 {
1270 _widget->viewSetPosition (NR::Point(x,y));
1271 }
1273 void
1274 SPDesktop::onResized (double /*x*/, double /*y*/)
1275 {
1276 // Nothing called here
1277 }
1279 /**
1280 * Redraw callback; queues Gtk redraw; connected by View.
1281 */
1282 void
1283 SPDesktop::onRedrawRequested ()
1284 {
1285 if (main) {
1286 _widget->requestCanvasUpdate();
1287 }
1288 }
1290 void
1291 SPDesktop::updateCanvasNow()
1292 {
1293 _widget->requestCanvasUpdateAndWait();
1294 }
1296 /**
1297 * Associate document with desktop.
1298 */
1299 void
1300 SPDesktop::setDocument (SPDocument *doc)
1301 {
1302 if (this->doc() && doc) {
1303 namedview->hide(this);
1304 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1305 }
1307 if (_layer_hierarchy) {
1308 _layer_hierarchy->clear();
1309 delete _layer_hierarchy;
1310 }
1311 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1312 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1313 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1314 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1315 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1317 /* setup EventLog */
1318 event_log = new Inkscape::EventLog(doc);
1319 doc->addUndoObserver(*event_log);
1321 _commit_connection.disconnect();
1322 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1324 /// \todo fixme: This condition exists to make sure the code
1325 /// inside is NOT called on initialization, only on replacement. But there
1326 /// are surely more safe methods to accomplish this.
1327 // TODO since the comment had reversed logic, check the intent of this block of code:
1328 if (drawing) {
1329 NRArenaItem *ai = 0;
1331 namedview = sp_document_namedview (doc, NULL);
1332 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1333 number = namedview->getViewCount();
1335 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1336 SP_CANVAS_ARENA (drawing)->arena,
1337 dkey,
1338 SP_ITEM_SHOW_DISPLAY);
1339 if (ai) {
1340 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1341 nr_arena_item_unref (ai);
1342 }
1343 namedview->show(this);
1344 /* Ugly hack */
1345 activate_guides (true);
1346 /* Ugly hack */
1347 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1348 }
1350 _document_replaced_signal.emit (this, doc);
1352 View::setDocument (doc);
1353 }
1355 void
1356 SPDesktop::onStatusMessage
1357 (Inkscape::MessageType type, gchar const *message)
1358 {
1359 if (_widget) {
1360 _widget->setMessage(type, message);
1361 }
1362 }
1364 void
1365 SPDesktop::onDocumentURISet (gchar const* uri)
1366 {
1367 _widget->setTitle(uri);
1368 }
1370 /**
1371 * Resized callback.
1372 */
1373 void
1374 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1375 {
1376 _doc2dt[5] = height;
1377 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1378 NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1379 SP_CTRLRECT(page)->setRectangle(a);
1380 SP_CTRLRECT(page_border)->setRectangle(a);
1381 }
1384 void
1385 SPDesktop::_onActivate (SPDesktop* dt)
1386 {
1387 if (!dt->_widget) return;
1388 dt->_widget->activateDesktop();
1389 }
1391 void
1392 SPDesktop::_onDeactivate (SPDesktop* dt)
1393 {
1394 if (!dt->_widget) return;
1395 dt->_widget->deactivateDesktop();
1396 }
1398 void
1399 SPDesktop::_onSelectionModified
1400 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1401 {
1402 if (!dt->_widget) return;
1403 dt->_widget->updateScrollbars (expansion(dt->_d2w));
1404 }
1406 static void
1407 _onSelectionChanged
1408 (Inkscape::Selection *selection, SPDesktop *desktop)
1409 {
1410 /** \todo
1411 * only change the layer for single selections, or what?
1412 * This seems reasonable -- for multiple selections there can be many
1413 * different layers involved.
1414 */
1415 SPItem *item=selection->singleItem();
1416 if (item) {
1417 SPObject *layer=desktop->layerForObject(item);
1418 if ( layer && layer != desktop->currentLayer() ) {
1419 desktop->setCurrentLayer(layer);
1420 }
1421 }
1422 }
1424 /**
1425 * Calls event handler of current event context.
1426 * \param arena Unused
1427 * \todo fixme
1428 */
1429 static gint
1430 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1431 {
1432 if (ai) {
1433 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1434 return sp_event_context_item_handler (desktop->event_context, spi, event);
1435 } else {
1436 return sp_event_context_root_handler (desktop->event_context, event);
1437 }
1438 }
1440 static void
1441 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1442 g_return_if_fail(SP_IS_GROUP(layer));
1443 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1444 }
1446 /// Callback
1447 static void
1448 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1449 g_return_if_fail(SP_IS_GROUP(layer));
1450 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1451 }
1453 /// Callback
1454 static void
1455 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1456 SPDesktop *desktop)
1457 {
1458 desktop->_layer_changed_signal.emit (bottom);
1459 }
1461 /// Called when document is starting to be rebuilt.
1462 static void
1463 _reconstruction_start (SPDesktop * desktop)
1464 {
1465 // printf("Desktop, starting reconstruction\n");
1466 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1467 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1469 /*
1470 GSList const * selection_objs = desktop->selection->list();
1471 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1473 }
1474 */
1475 desktop->selection->clear();
1477 // printf("Desktop, starting reconstruction end\n");
1478 }
1480 /// Called when document rebuild is finished.
1481 static void
1482 _reconstruction_finish (SPDesktop * desktop)
1483 {
1484 // printf("Desktop, finishing reconstruction\n");
1485 if (desktop->_reconstruction_old_layer_id == NULL)
1486 return;
1488 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1489 if (newLayer != NULL)
1490 desktop->setCurrentLayer(newLayer);
1492 g_free(desktop->_reconstruction_old_layer_id);
1493 desktop->_reconstruction_old_layer_id = NULL;
1494 // printf("Desktop, finishing reconstruction end\n");
1495 return;
1496 }
1498 /**
1499 * Namedview_modified callback.
1500 */
1501 static void
1502 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1503 {
1504 SPNamedView *nv=SP_NAMEDVIEW(obj);
1506 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1508 /* Recalculate snap distances */
1509 /* FIXME: why is the desktop getting involved in setting up something
1510 ** that is entirely to do with the namedview?
1511 */
1512 _update_snap_distances (desktop);
1514 /* Show/hide page background */
1515 if (nv->pagecolor & 0xff) {
1516 sp_canvas_item_show (desktop->table);
1517 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1518 sp_canvas_item_move_to_z (desktop->table, 0);
1519 } else {
1520 sp_canvas_item_hide (desktop->table);
1521 }
1523 /* Show/hide page border */
1524 if (nv->showborder) {
1525 // show
1526 sp_canvas_item_show (desktop->page_border);
1527 // set color and shadow
1528 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1529 if (nv->pageshadow) {
1530 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1531 }
1532 // place in the z-order stack
1533 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1534 sp_canvas_item_move_to_z (desktop->page_border, 2);
1535 } else {
1536 int order = sp_canvas_item_order (desktop->page_border);
1537 int morder = sp_canvas_item_order (desktop->drawing);
1538 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1539 morder - order);
1540 }
1541 } else {
1542 sp_canvas_item_hide (desktop->page_border);
1543 if (nv->pageshadow) {
1544 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1545 }
1546 }
1548 /* Show/hide page shadow */
1549 if (nv->showpageshadow && nv->pageshadow) {
1550 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1551 } else {
1552 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1553 }
1555 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1556 (SP_RGBA32_R_U(nv->pagecolor) +
1557 SP_RGBA32_G_U(nv->pagecolor) +
1558 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1559 // the background color is light or transparent, use black outline
1560 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1561 } else { // use white outline
1562 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1563 }
1564 }
1565 }
1567 /**
1568 * Callback to reset snapper's distances.
1569 */
1570 static void
1571 _update_snap_distances (SPDesktop *desktop)
1572 {
1573 SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1575 SPNamedView &nv = *desktop->namedview;
1577 //tell all grid snappers
1578 for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1579 Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1580 grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
1581 *nv.gridtoleranceunit,
1582 px));
1583 }
1585 nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
1586 *nv.guidetoleranceunit,
1587 px));
1588 nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
1589 *nv.objecttoleranceunit,
1590 px));
1591 }
1594 NR::Matrix SPDesktop::w2d() const
1595 {
1596 return _w2d;
1597 }
1599 NR::Point SPDesktop::w2d(NR::Point const &p) const
1600 {
1601 return p * _w2d;
1602 }
1604 NR::Point SPDesktop::d2w(NR::Point const &p) const
1605 {
1606 return p * _d2w;
1607 }
1609 NR::Matrix SPDesktop::doc2dt() const
1610 {
1611 return _doc2dt;
1612 }
1614 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1615 {
1616 return p * _doc2dt;
1617 }
1619 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1620 {
1621 return p / _doc2dt;
1622 }
1625 /**
1626 * Pop event context from desktop's context stack. Never used.
1627 */
1628 // void
1629 // SPDesktop::pop_event_context (unsigned int key)
1630 // {
1631 // SPEventContext *ec = NULL;
1632 //
1633 // if (event_context && event_context->key == key) {
1634 // g_return_if_fail (event_context);
1635 // g_return_if_fail (event_context->next);
1636 // ec = event_context;
1637 // sp_event_context_deactivate (ec);
1638 // event_context = ec->next;
1639 // sp_event_context_activate (event_context);
1640 // _event_context_changed_signal.emit (this, ec);
1641 // }
1642 //
1643 // SPEventContext *ref = event_context;
1644 // while (ref && ref->next && ref->next->key != key)
1645 // ref = ref->next;
1646 //
1647 // if (ref && ref->next) {
1648 // ec = ref->next;
1649 // ref->next = ec->next;
1650 // }
1651 //
1652 // if (ec) {
1653 // sp_event_context_finish (ec);
1654 // g_object_unref (G_OBJECT (ec));
1655 // }
1656 // }
1658 /*
1659 Local Variables:
1660 mode:c++
1661 c-file-style:"stroustrup"
1662 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1663 indent-tabs-mode:nil
1664 fill-column:99
1665 End:
1666 */
1667 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :