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-2007 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 "libnr/nr-matrix-div.h"
81 #include "libnr/nr-rect-ops.h"
82 #include "ui/dialog/dialog-manager.h"
83 #include "xml/repr.h"
84 #include "message-context.h"
85 #include "layer-manager.h"
86 #include "event-log.h"
87 #include "display/canvas-grid.h"
89 #include "display/sp-canvas.h"
91 namespace Inkscape { namespace XML { class Node; }}
93 // Callback declarations
94 static void _onSelectionChanged (Inkscape::Selection *selection, SPDesktop *desktop);
95 static gint _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop);
96 static void _layer_activated(SPObject *layer, SPDesktop *desktop);
97 static void _layer_deactivated(SPObject *layer, SPDesktop *desktop);
98 static void _layer_hierarchy_changed(SPObject *top, SPObject *bottom, SPDesktop *desktop);
99 static void _reconstruction_start(SPDesktop * desktop);
100 static void _reconstruction_finish(SPDesktop * desktop);
101 static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop);
102 static void _update_snap_distances (SPDesktop *desktop);
104 /**
105 * Return new desktop object.
106 * \pre namedview != NULL.
107 * \pre canvas != NULL.
108 */
109 SPDesktop::SPDesktop() :
110 _dlg_mgr( 0 ),
111 namedview( 0 ),
112 canvas( 0 ),
113 selection( 0 ),
114 event_context( 0 ),
115 layer_manager( 0 ),
116 event_log( 0 ),
117 acetate( 0 ),
118 main( 0 ),
119 gridgroup( 0 ),
120 guides( 0 ),
121 drawing( 0 ),
122 sketch( 0 ),
123 controls( 0 ),
124 table( 0 ),
125 page( 0 ),
126 page_border( 0 ),
127 current( 0 ),
128 zooms_past( 0 ),
129 zooms_future( 0 ),
130 dkey( 0 ),
131 number( 0 ),
132 window_state(0),
133 interaction_disabled_counter( 0 ),
134 waiting_cursor( false ),
135 guides_active( false ),
136 gr_item( 0 ),
137 gr_point_type( 0 ),
138 gr_point_i( 0 ),
139 gr_fill_or_stroke( true ),
140 _layer_hierarchy( 0 ),
141 _reconstruction_old_layer_id( 0 ),
142 _widget( 0 ),
143 _inkscape( 0 ),
144 _guides_message_context( 0 ),
145 _active( false ),
146 _w2d(),
147 _d2w(),
148 _doc2dt( NR::Matrix(NR::scale(1, -1)) ),
149 grids_visible( true )
150 {
151 _d2w.set_identity();
152 _w2d.set_identity();
154 selection = Inkscape::GC::release( new Inkscape::Selection(this) );
155 }
157 void
158 SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas)
159 {
160 _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
162 current = sp_repr_css_attr_inherited (inkscape_get_repr (INKSCAPE, "desktop"), "style");
164 namedview = nv;
165 canvas = aCanvas;
167 SPDocument *document = SP_OBJECT_DOCUMENT (namedview);
168 /* Kill flicker */
169 sp_document_ensure_up_to_date (document);
171 /* Setup Dialog Manager */
172 _dlg_mgr = new Inkscape::UI::Dialog::DialogManager();
174 dkey = sp_item_display_key_new (1);
176 /* Connect document */
177 setDocument (document);
179 number = namedview->getViewCount();
182 /* Setup Canvas */
183 g_object_set_data (G_OBJECT (canvas), "SPDesktop", this);
185 SPCanvasGroup *root = sp_canvas_root (canvas);
187 /* Setup adminstrative layers */
188 acetate = sp_canvas_item_new (root, GNOME_TYPE_CANVAS_ACETATE, NULL);
189 g_signal_connect (G_OBJECT (acetate), "event", G_CALLBACK (sp_desktop_root_handler), this);
190 main = (SPCanvasGroup *) sp_canvas_item_new (root, SP_TYPE_CANVAS_GROUP, NULL);
191 g_signal_connect (G_OBJECT (main), "event", G_CALLBACK (sp_desktop_root_handler), this);
193 table = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
194 SP_CTRLRECT(table)->setRectangle(NR::Rect(NR::Point(-80000, -80000), NR::Point(80000, 80000)));
195 SP_CTRLRECT(table)->setColor(0x00000000, true, 0x00000000);
196 sp_canvas_item_move_to_z (table, 0);
198 page = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
199 ((CtrlRect *) page)->setColor(0x00000000, FALSE, 0x00000000);
200 page_border = sp_canvas_item_new (main, SP_TYPE_CTRLRECT, NULL);
202 drawing = sp_canvas_item_new (main, SP_TYPE_CANVAS_ARENA, NULL);
203 g_signal_connect (G_OBJECT (drawing), "arena_event", G_CALLBACK (_arena_handler), this);
205 SP_CANVAS_ARENA (drawing)->arena->delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0); // default is 1 px
207 if (prefs_get_int_attribute("options.startmode", "outline", 0)) {
208 // Start in outline mode
209 setDisplayModeOutline();
210 } else {
211 // Start in normal mode, default
212 setDisplayModeNormal();
213 }
215 gridgroup = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
216 guides = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
217 sketch = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
218 controls = (SPCanvasGroup *) sp_canvas_item_new (main, SP_TYPE_CANVAS_GROUP, NULL);
220 /* Push select tool to the bottom of stack */
221 /** \todo
222 * FIXME: this is the only call to this. Everything else seems to just
223 * call "set" instead of "push". Can we assume that there is only one
224 * context ever?
225 */
226 push_event_context (SP_TYPE_SELECT_CONTEXT, "tools.select", SP_EVENT_CONTEXT_STATIC);
228 // display rect and zoom are now handled in sp_desktop_widget_realize()
230 NR::Rect const d(NR::Point(0.0, 0.0),
231 NR::Point(sp_document_width(document), sp_document_height(document)));
233 SP_CTRLRECT(page)->setRectangle(d);
234 SP_CTRLRECT(page_border)->setRectangle(d);
236 /* the following sets the page shadow on the canvas
237 It was originally set to 5, which is really cheesy!
238 It now is an attribute in the document's namedview. If a value of
239 0 is used, then the constructor for a shadow is not initialized.
240 */
242 if ( namedview->pageshadow != 0 && namedview->showpageshadow ) {
243 SP_CTRLRECT(page_border)->setShadow(namedview->pageshadow, 0x3f3f3fff);
244 }
247 /* Connect event for page resize */
248 _doc2dt[5] = sp_document_height (document);
249 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
251 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
253 NRArenaItem *ai = sp_item_invoke_show (SP_ITEM (sp_document_root (document)),
254 SP_CANVAS_ARENA (drawing)->arena,
255 dkey,
256 SP_ITEM_SHOW_DISPLAY);
257 if (ai) {
258 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
259 nr_arena_item_unref (ai);
260 }
262 namedview->show(this);
263 /* Ugly hack */
264 activate_guides (true);
265 /* Ugly hack */
266 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
268 /* Set up notification of rebuilding the document, this allows
269 for saving object related settings in the document. */
270 _reconstruction_start_connection =
271 document->connectReconstructionStart(sigc::bind(sigc::ptr_fun(_reconstruction_start), this));
272 _reconstruction_finish_connection =
273 document->connectReconstructionFinish(sigc::bind(sigc::ptr_fun(_reconstruction_finish), this));
274 _reconstruction_old_layer_id = NULL;
276 // ?
277 // sp_active_desktop_set (desktop);
278 _inkscape = INKSCAPE;
280 _activate_connection = _activate_signal.connect(
281 sigc::bind(
282 sigc::ptr_fun(_onActivate),
283 this
284 )
285 );
286 _deactivate_connection = _deactivate_signal.connect(
287 sigc::bind(
288 sigc::ptr_fun(_onDeactivate),
289 this
290 )
291 );
293 _sel_modified_connection = selection->connectModified(
294 sigc::bind(
295 sigc::ptr_fun(&_onSelectionModified),
296 this
297 )
298 );
299 _sel_changed_connection = selection->connectChanged(
300 sigc::bind(
301 sigc::ptr_fun(&_onSelectionChanged),
302 this
303 )
304 );
307 /* setup LayerManager */
308 // (Setting up after the connections are all in place, as it may use some of them)
309 layer_manager = new Inkscape::LayerManager( this );
311 grids_visible = true;
312 }
315 void SPDesktop::destroy()
316 {
317 _activate_connection.disconnect();
318 _deactivate_connection.disconnect();
319 _sel_modified_connection.disconnect();
320 _sel_changed_connection.disconnect();
321 _modified_connection.disconnect();
322 _commit_connection.disconnect();
323 _reconstruction_start_connection.disconnect();
324 _reconstruction_finish_connection.disconnect();
326 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
327 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
328 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
330 while (event_context) {
331 SPEventContext *ec = event_context;
332 event_context = ec->next;
333 sp_event_context_finish (ec);
334 g_object_unref (G_OBJECT (ec));
335 }
337 if (_layer_hierarchy) {
338 delete _layer_hierarchy;
339 }
341 if (_inkscape) {
342 _inkscape = NULL;
343 }
345 if (drawing) {
346 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
347 drawing = NULL;
348 }
350 delete _guides_message_context;
351 _guides_message_context = NULL;
353 g_list_free (zooms_past);
354 g_list_free (zooms_future);
355 }
357 SPDesktop::~SPDesktop() {}
359 //--------------------------------------------------------------------
360 /* Public methods */
362 void SPDesktop::setDisplayModeNormal()
363 {
364 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
365 canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
366 displayMode = RENDERMODE_NORMAL;
367 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
368 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
369 }
371 void SPDesktop::setDisplayModeOutline()
372 {
373 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
374 canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
375 displayMode = RENDERMODE_OUTLINE;
376 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
377 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
378 }
380 void SPDesktop::displayModeToggle()
381 {
382 if (displayMode == RENDERMODE_OUTLINE)
383 setDisplayModeNormal();
384 else
385 setDisplayModeOutline();
386 }
388 /**
389 * Returns current root (=bottom) layer.
390 */
391 SPObject *SPDesktop::currentRoot() const
392 {
393 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
394 }
396 /**
397 * Returns current top layer.
398 */
399 SPObject *SPDesktop::currentLayer() const
400 {
401 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
402 }
404 /**
405 * Sets the current layer of the desktop.
406 *
407 * Make \a object the top layer.
408 */
409 void SPDesktop::setCurrentLayer(SPObject *object) {
410 g_return_if_fail(SP_IS_GROUP(object));
411 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
412 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
413 _layer_hierarchy->setBottom(object);
414 }
416 /**
417 * Return layer that contains \a object.
418 */
419 SPObject *SPDesktop::layerForObject(SPObject *object) {
420 g_return_val_if_fail(object != NULL, NULL);
422 SPObject *root=currentRoot();
423 object = SP_OBJECT_PARENT(object);
424 while ( object && object != root && !isLayer(object) ) {
425 object = SP_OBJECT_PARENT(object);
426 }
427 return object;
428 }
430 /**
431 * True if object is a layer.
432 */
433 bool SPDesktop::isLayer(SPObject *object) const {
434 return ( SP_IS_GROUP(object)
435 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
436 == SPGroup::LAYER ) );
437 }
439 /**
440 * True if desktop viewport fully contains \a item's bbox.
441 */
442 bool SPDesktop::isWithinViewport (SPItem *item) const
443 {
444 NR::Rect const viewport = get_display_area();
445 NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
446 if (bbox) {
447 return viewport.contains(*bbox);
448 } else {
449 return true;
450 }
451 }
453 ///
454 bool SPDesktop::itemIsHidden(SPItem const *item) const {
455 return item->isHidden(this->dkey);
456 }
458 /**
459 * Set activate property of desktop; emit signal if changed.
460 */
461 void
462 SPDesktop::set_active (bool new_active)
463 {
464 if (new_active != _active) {
465 _active = new_active;
466 if (new_active) {
467 _activate_signal.emit();
468 } else {
469 _deactivate_signal.emit();
470 }
471 }
472 }
474 /**
475 * Set activate status of current desktop's named view.
476 */
477 void
478 SPDesktop::activate_guides(bool activate)
479 {
480 guides_active = activate;
481 namedview->activateGuides(this, activate);
482 }
484 /**
485 * Make desktop switch documents.
486 */
487 void
488 SPDesktop::change_document (SPDocument *theDocument)
489 {
490 g_return_if_fail (theDocument != NULL);
492 /* unselect everything before switching documents */
493 selection->clear();
495 setDocument (theDocument);
496 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
497 _document_replaced_signal.emit (this, theDocument);
498 }
500 /**
501 * Make desktop switch event contexts.
502 */
503 void
504 SPDesktop::set_event_context (GtkType type, const gchar *config)
505 {
506 SPEventContext *ec;
507 while (event_context) {
508 ec = event_context;
509 sp_event_context_deactivate (ec);
510 event_context = ec->next;
511 sp_event_context_finish (ec);
512 g_object_unref (G_OBJECT (ec));
513 }
515 Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
516 ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
517 ec->next = event_context;
518 event_context = ec;
519 sp_event_context_activate (ec);
520 _event_context_changed_signal.emit (this, ec);
521 }
523 /**
524 * Push event context onto desktop's context stack.
525 */
526 void
527 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
528 {
529 SPEventContext *ref, *ec;
530 Inkscape::XML::Node *repr;
532 if (event_context && event_context->key == key) return;
533 ref = event_context;
534 while (ref && ref->next && ref->next->key != key) ref = ref->next;
535 if (ref && ref->next) {
536 ec = ref->next;
537 ref->next = ec->next;
538 sp_event_context_finish (ec);
539 g_object_unref (G_OBJECT (ec));
540 }
542 if (event_context) sp_event_context_deactivate (event_context);
543 repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
544 ec = sp_event_context_new (type, this, repr, key);
545 ec->next = event_context;
546 event_context = ec;
547 sp_event_context_activate (ec);
548 _event_context_changed_signal.emit (this, ec);
549 }
551 /**
552 * Sets the coordinate status to a given point
553 */
554 void
555 SPDesktop::set_coordinate_status (NR::Point p) {
556 _widget->setCoordinateStatus(p);
557 }
559 /**
560 * \see sp_document_item_from_list_at_point_bottom()
561 */
562 SPItem *
563 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
564 {
565 g_return_val_if_fail (doc() != NULL, NULL);
566 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
567 }
569 /**
570 * \see sp_document_item_at_point()
571 */
572 SPItem *
573 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
574 {
575 g_return_val_if_fail (doc() != NULL, NULL);
576 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
577 }
579 /**
580 * \see sp_document_group_at_point()
581 */
582 SPItem *
583 SPDesktop::group_at_point (NR::Point const p) const
584 {
585 g_return_val_if_fail (doc() != NULL, NULL);
586 return sp_document_group_at_point (doc(), dkey, p);
587 }
589 /**
590 * \brief Returns the mouse point in document coordinates; if mouse is
591 * outside the canvas, returns the center of canvas viewpoint
592 */
593 NR::Point
594 SPDesktop::point() const
595 {
596 NR::Point p = _widget->getPointer();
597 NR::Point pw = sp_canvas_window_to_world (canvas, p);
598 p = w2d(pw);
600 NR::Rect const r = canvas->getViewbox();
602 NR::Point r0 = w2d(r.min());
603 NR::Point r1 = w2d(r.max());
605 if (p[NR::X] >= r0[NR::X] &&
606 p[NR::X] <= r1[NR::X] &&
607 p[NR::Y] >= r1[NR::Y] &&
608 p[NR::Y] <= r0[NR::Y])
609 {
610 return p;
611 } else {
612 return (r0 + r1) / 2;
613 }
614 }
616 /**
617 * Put current zoom data in history list.
618 */
619 void
620 SPDesktop::push_current_zoom (GList **history)
621 {
622 NR::Rect const area = get_display_area();
624 NRRect *old_zoom = g_new(NRRect, 1);
625 old_zoom->x0 = area.min()[NR::X];
626 old_zoom->x1 = area.max()[NR::X];
627 old_zoom->y0 = area.min()[NR::Y];
628 old_zoom->y1 = area.max()[NR::Y];
629 if ( *history == NULL
630 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
631 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
632 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
633 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
634 {
635 *history = g_list_prepend (*history, old_zoom);
636 }
637 }
639 /**
640 * Set viewbox.
641 */
642 void
643 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
644 {
645 g_assert(_widget);
647 // save the zoom
648 if (log) {
649 push_current_zoom(&zooms_past);
650 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
651 g_list_free (zooms_future);
652 zooms_future = NULL;
653 }
655 double const cx = 0.5 * (x0 + x1);
656 double const cy = 0.5 * (y0 + y1);
658 NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
660 double scale = expansion(_d2w);
661 double newscale;
662 if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
663 newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
664 } else {
665 newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
666 }
668 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
670 int clear = FALSE;
671 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
672 /* Set zoom factors */
673 _d2w = NR::Matrix(NR::scale(newscale, -newscale));
674 _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
675 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
676 clear = TRUE;
677 }
679 /* Calculate top left corner */
680 x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
681 y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
683 /* Scroll */
684 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
686 _widget->updateRulers();
687 _widget->updateScrollbars(expansion(_d2w));
688 _widget->updateZoom();
689 }
691 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
692 {
693 set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
694 }
696 /**
697 * Return viewbox dimensions.
698 */
699 NR::Rect SPDesktop::get_display_area() const
700 {
701 NR::Rect const viewbox = canvas->getViewbox();
703 double const scale = _d2w[0];
705 return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
706 NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
707 }
709 /**
710 * Revert back to previous zoom if possible.
711 */
712 void
713 SPDesktop::prev_zoom()
714 {
715 if (zooms_past == NULL) {
716 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
717 return;
718 }
720 // push current zoom into forward zooms list
721 push_current_zoom (&zooms_future);
723 // restore previous zoom
724 set_display_area (((NRRect *) zooms_past->data)->x0,
725 ((NRRect *) zooms_past->data)->y0,
726 ((NRRect *) zooms_past->data)->x1,
727 ((NRRect *) zooms_past->data)->y1,
728 0, false);
730 // remove the just-added zoom from the past zooms list
731 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
732 }
734 /**
735 * Set zoom to next in list.
736 */
737 void
738 SPDesktop::next_zoom()
739 {
740 if (zooms_future == NULL) {
741 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
742 return;
743 }
745 // push current zoom into past zooms list
746 push_current_zoom (&zooms_past);
748 // restore next zoom
749 set_display_area (((NRRect *) zooms_future->data)->x0,
750 ((NRRect *) zooms_future->data)->y0,
751 ((NRRect *) zooms_future->data)->x1,
752 ((NRRect *) zooms_future->data)->y1,
753 0, false);
755 // remove the just-used zoom from the zooms_future list
756 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
757 }
759 /**
760 * Zoom to point with absolute zoom factor.
761 */
762 void
763 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
764 {
765 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
767 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
768 // this check prevents "sliding" when trying to zoom in at maximum zoom;
769 /// \todo someone please fix calculations properly and remove this hack
770 if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
771 return;
773 NR::Rect const viewbox = canvas->getViewbox();
775 double const width2 = viewbox.dimensions()[NR::X] / zoom;
776 double const height2 = viewbox.dimensions()[NR::Y] / zoom;
778 set_display_area(cx - px * width2,
779 cy - py * height2,
780 cx + (1 - px) * width2,
781 cy + (1 - py) * height2,
782 0.0);
783 }
785 /**
786 * Zoom to center with absolute zoom factor.
787 */
788 void
789 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
790 {
791 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
792 }
794 /**
795 * Zoom to point with relative zoom factor.
796 */
797 void
798 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
799 {
800 NR::Rect const area = get_display_area();
802 if (cx < area.min()[NR::X]) {
803 cx = area.min()[NR::X];
804 }
805 if (cx > area.max()[NR::X]) {
806 cx = area.max()[NR::X];
807 }
808 if (cy < area.min()[NR::Y]) {
809 cy = area.min()[NR::Y];
810 }
811 if (cy > area.max()[NR::Y]) {
812 cy = area.max()[NR::Y];
813 }
815 gdouble const scale = expansion(_d2w) * zoom;
816 double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
817 double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
819 zoom_absolute_keep_point(cx, cy, px, py, scale);
820 }
822 /**
823 * Zoom to center with relative zoom factor.
824 */
825 void
826 SPDesktop::zoom_relative (double cx, double cy, double zoom)
827 {
828 gdouble scale = expansion(_d2w) * zoom;
829 zoom_absolute (cx, cy, scale);
830 }
832 /**
833 * Set display area to origin and current document dimensions.
834 */
835 void
836 SPDesktop::zoom_page()
837 {
838 NR::Rect d(NR::Point(0, 0),
839 NR::Point(sp_document_width(doc()), sp_document_height(doc())));
841 if (d.isEmpty(1.0)) {
842 return;
843 }
845 set_display_area(d, 10);
846 }
848 /**
849 * Set display area to current document width.
850 */
851 void
852 SPDesktop::zoom_page_width()
853 {
854 NR::Rect const a = get_display_area();
856 if (sp_document_width(doc()) < 1.0) {
857 return;
858 }
860 NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
861 NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
863 set_display_area(d, 10);
864 }
866 /**
867 * Zoom to selection.
868 */
869 void
870 SPDesktop::zoom_selection()
871 {
872 NR::Maybe<NR::Rect> const d = selection->bounds();
874 if ( !d || d->isEmpty(0.1) ) {
875 return;
876 }
878 set_display_area(*d, 10);
879 }
881 /**
882 * Tell widget to let zoom widget grab keyboard focus.
883 */
884 void
885 SPDesktop::zoom_grab_focus()
886 {
887 _widget->letZoomGrabFocus();
888 }
890 /**
891 * Zoom to whole drawing.
892 */
893 void
894 SPDesktop::zoom_drawing()
895 {
896 g_return_if_fail (doc() != NULL);
897 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
898 g_return_if_fail (docitem != NULL);
900 NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
902 /* Note that the second condition here indicates that
903 ** there are no items in the drawing.
904 */
905 if ( !d || d->isEmpty(1.0) ) {
906 return;
907 }
909 set_display_area(*d, 10);
910 }
912 /**
913 * Scroll canvas by specific coordinate amount.
914 */
915 void
916 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
917 {
918 g_assert(_widget);
920 NR::Rect const viewbox = canvas->getViewbox();
922 sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
924 _widget->updateRulers();
925 _widget->updateScrollbars(expansion(_d2w));
926 }
928 bool
929 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
930 {
931 gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
933 // autoscrolldistance is in screen pixels, but the display area is in document units
934 autoscrolldistance /= expansion(_d2w);
935 NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
937 if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
938 !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y]) ) {
940 NR::Point const s_w( (*p) * _d2w );
942 gdouble x_to;
943 if ((*p)[NR::X] < dbox.min()[NR::X])
944 x_to = dbox.min()[NR::X];
945 else if ((*p)[NR::X] > dbox.max()[NR::X])
946 x_to = dbox.max()[NR::X];
947 else
948 x_to = (*p)[NR::X];
950 gdouble y_to;
951 if ((*p)[NR::Y] < dbox.min()[NR::Y])
952 y_to = dbox.min()[NR::Y];
953 else if ((*p)[NR::Y] > dbox.max()[NR::Y])
954 y_to = dbox.max()[NR::Y];
955 else
956 y_to = (*p)[NR::Y];
958 NR::Point const d_dt(x_to, y_to);
959 NR::Point const d_w( d_dt * _d2w );
960 NR::Point const moved_w( d_w - s_w );
962 if (autoscrollspeed == 0)
963 autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
965 if (autoscrollspeed != 0)
966 scroll_world (autoscrollspeed * moved_w);
968 return true;
969 }
970 return false;
971 }
973 bool
974 SPDesktop::is_iconified()
975 {
976 return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
977 }
979 void
980 SPDesktop::iconify()
981 {
982 _widget->setIconified();
983 }
985 bool
986 SPDesktop::is_maximized()
987 {
988 return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
989 }
991 void
992 SPDesktop::maximize()
993 {
994 _widget->setMaximized();
995 }
997 bool
998 SPDesktop::is_fullscreen()
999 {
1000 return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1001 }
1003 void
1004 SPDesktop::fullscreen()
1005 {
1006 _widget->setFullscreen();
1007 }
1009 void
1010 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1011 {
1012 _widget->getGeometry (x, y, w, h);
1013 }
1015 void
1016 SPDesktop::setWindowPosition (NR::Point p)
1017 {
1018 _widget->setPosition (p);
1019 }
1021 void
1022 SPDesktop::setWindowSize (gint w, gint h)
1023 {
1024 _widget->setSize (w, h);
1025 }
1027 void
1028 SPDesktop::setWindowTransient (void *p, int transient_policy)
1029 {
1030 _widget->setTransient (p, transient_policy);
1031 }
1033 Gtk::Window*
1034 SPDesktop::getToplevel( )
1035 {
1036 return _widget->getWindow();
1037 }
1039 void
1040 SPDesktop::presentWindow()
1041 {
1042 _widget->present();
1043 }
1045 bool
1046 SPDesktop::warnDialog (gchar *text)
1047 {
1048 return _widget->warnDialog (text);
1049 }
1051 void
1052 SPDesktop::toggleRulers()
1053 {
1054 _widget->toggleRulers();
1055 }
1057 void
1058 SPDesktop::toggleScrollbars()
1059 {
1060 _widget->toggleScrollbars();
1061 }
1063 void
1064 SPDesktop::layoutWidget()
1065 {
1066 _widget->layout();
1067 }
1069 void
1070 SPDesktop::destroyWidget()
1071 {
1072 _widget->destroy();
1073 }
1075 bool
1076 SPDesktop::shutdown()
1077 {
1078 return _widget->shutdown();
1079 }
1081 bool SPDesktop::onDeleteUI (GdkEventAny*)
1082 {
1083 if(shutdown()) return true;
1084 destroyWidget();
1085 return false;
1086 }
1088 /**
1089 * onWindowStateEvent
1090 *
1091 * Called when the window changes its maximize/fullscreen/iconify/pinned state.
1092 * Since GTK doesn't have a way to query this state information directly, we
1093 * record it for the desktop here, and also possibly trigger a layout.
1094 */
1095 bool
1096 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1097 {
1098 // Record the desktop window's state
1099 window_state = event->new_window_state;
1101 // Layout may differ depending on full-screen mode or not
1102 GdkWindowState changed = event->changed_mask;
1103 if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1104 layoutWidget();
1105 }
1107 return false;
1108 }
1110 void
1111 SPDesktop::setToolboxFocusTo (gchar const *label)
1112 {
1113 _widget->setToolboxFocusTo (label);
1114 }
1116 void
1117 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1118 {
1119 _widget->setToolboxAdjustmentValue (id, val);
1120 }
1122 void
1123 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1124 {
1125 _widget->setToolboxSelectOneValue (id, val);
1126 }
1128 bool
1129 SPDesktop::isToolboxButtonActive (gchar const *id)
1130 {
1131 return _widget->isToolboxButtonActive (id);
1132 }
1134 void
1135 SPDesktop::emitToolSubselectionChanged(gpointer data)
1136 {
1137 _tool_subselection_changed.emit(data);
1138 inkscape_subselection_changed (this);
1139 }
1141 void
1142 SPDesktop::updateNow()
1143 {
1144 sp_canvas_update_now(canvas);
1145 }
1147 void
1148 SPDesktop::enableInteraction()
1149 {
1150 _widget->enableInteraction();
1151 }
1153 void SPDesktop::disableInteraction()
1154 {
1155 _widget->disableInteraction();
1156 }
1158 void SPDesktop::setWaitingCursor()
1159 {
1160 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1161 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1162 gdk_cursor_unref(waiting);
1163 waiting_cursor = true;
1165 // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1166 // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1167 // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1168 // after the call to setWaitingCursor as it was before
1169 while( Gtk::Main::events_pending() )
1170 Gtk::Main::iteration();
1171 }
1173 void SPDesktop::clearWaitingCursor()
1174 {
1175 if (waiting_cursor)
1176 sp_event_context_update_cursor(sp_desktop_event_context(this));
1177 }
1179 void SPDesktop::toggleGrid()
1180 {
1181 if (namedview->grids) {
1182 if(gridgroup) {
1183 grids_visible = !grids_visible;
1184 if (grids_visible) {
1185 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1186 } else {
1187 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1188 }
1189 }
1190 } else {
1191 //there is no grid present at the moment. add a rectangular grid and make it visible
1192 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1193 Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1194 grids_visible = true;
1195 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1196 }
1197 }
1200 //----------------------------------------------------------------------
1201 // Callback implementations. The virtual ones are connected by the view.
1203 void
1204 SPDesktop::onPositionSet (double x, double y)
1205 {
1206 _widget->viewSetPosition (NR::Point(x,y));
1207 }
1209 void
1210 SPDesktop::onResized (double x, double y)
1211 {
1212 // Nothing called here
1213 }
1215 /**
1216 * Redraw callback; queues Gtk redraw; connected by View.
1217 */
1218 void
1219 SPDesktop::onRedrawRequested ()
1220 {
1221 if (main) {
1222 _widget->requestCanvasUpdate();
1223 }
1224 }
1226 void
1227 SPDesktop::updateCanvasNow()
1228 {
1229 _widget->requestCanvasUpdateAndWait();
1230 }
1232 /**
1233 * Associate document with desktop.
1234 */
1235 void
1236 SPDesktop::setDocument (SPDocument *doc)
1237 {
1238 if (this->doc() && doc) {
1239 namedview->hide(this);
1240 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1241 }
1243 if (_layer_hierarchy) {
1244 _layer_hierarchy->clear();
1245 delete _layer_hierarchy;
1246 }
1247 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1248 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1249 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1250 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1251 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1253 /* setup EventLog */
1254 event_log = new Inkscape::EventLog(doc);
1255 doc->addUndoObserver(*event_log);
1257 _commit_connection.disconnect();
1258 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1260 /// \todo fixme: This condition exists to make sure the code
1261 /// inside is NOT called on initialization, only on replacement. But there
1262 /// are surely more safe methods to accomplish this.
1263 // TODO since the comment had reversed logic, check the intent of this block of code:
1264 if (drawing) {
1265 NRArenaItem *ai = 0;
1267 namedview = sp_document_namedview (doc, NULL);
1268 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1269 number = namedview->getViewCount();
1271 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1272 SP_CANVAS_ARENA (drawing)->arena,
1273 dkey,
1274 SP_ITEM_SHOW_DISPLAY);
1275 if (ai) {
1276 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1277 nr_arena_item_unref (ai);
1278 }
1279 namedview->show(this);
1280 /* Ugly hack */
1281 activate_guides (true);
1282 /* Ugly hack */
1283 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1284 }
1286 _document_replaced_signal.emit (this, doc);
1288 View::setDocument (doc);
1289 }
1291 void
1292 SPDesktop::onStatusMessage
1293 (Inkscape::MessageType type, gchar const *message)
1294 {
1295 if (_widget) {
1296 _widget->setMessage(type, message);
1297 }
1298 }
1300 void
1301 SPDesktop::onDocumentURISet (gchar const* uri)
1302 {
1303 _widget->setTitle(uri);
1304 }
1306 /**
1307 * Resized callback.
1308 */
1309 void
1310 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1311 {
1312 _doc2dt[5] = height;
1313 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1314 NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1315 SP_CTRLRECT(page)->setRectangle(a);
1316 SP_CTRLRECT(page_border)->setRectangle(a);
1317 }
1320 void
1321 SPDesktop::_onActivate (SPDesktop* dt)
1322 {
1323 if (!dt->_widget) return;
1324 dt->_widget->activateDesktop();
1325 }
1327 void
1328 SPDesktop::_onDeactivate (SPDesktop* dt)
1329 {
1330 if (!dt->_widget) return;
1331 dt->_widget->deactivateDesktop();
1332 }
1334 void
1335 SPDesktop::_onSelectionModified
1336 (Inkscape::Selection *selection, guint flags, SPDesktop *dt)
1337 {
1338 if (!dt->_widget) return;
1339 dt->_widget->updateScrollbars (expansion(dt->_d2w));
1340 }
1342 static void
1343 _onSelectionChanged
1344 (Inkscape::Selection *selection, SPDesktop *desktop)
1345 {
1346 /** \todo
1347 * only change the layer for single selections, or what?
1348 * This seems reasonable -- for multiple selections there can be many
1349 * different layers involved.
1350 */
1351 SPItem *item=selection->singleItem();
1352 if (item) {
1353 SPObject *layer=desktop->layerForObject(item);
1354 if ( layer && layer != desktop->currentLayer() ) {
1355 desktop->setCurrentLayer(layer);
1356 }
1357 }
1358 }
1360 /**
1361 * Calls event handler of current event context.
1362 * \param arena Unused
1363 * \todo fixme
1364 */
1365 static gint
1366 _arena_handler (SPCanvasArena *arena, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1367 {
1368 if (ai) {
1369 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1370 return sp_event_context_item_handler (desktop->event_context, spi, event);
1371 } else {
1372 return sp_event_context_root_handler (desktop->event_context, event);
1373 }
1374 }
1376 static void
1377 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1378 g_return_if_fail(SP_IS_GROUP(layer));
1379 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1380 }
1382 /// Callback
1383 static void
1384 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1385 g_return_if_fail(SP_IS_GROUP(layer));
1386 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1387 }
1389 /// Callback
1390 static void
1391 _layer_hierarchy_changed(SPObject *top, SPObject *bottom,
1392 SPDesktop *desktop)
1393 {
1394 desktop->_layer_changed_signal.emit (bottom);
1395 }
1397 /// Called when document is starting to be rebuilt.
1398 static void
1399 _reconstruction_start (SPDesktop * desktop)
1400 {
1401 // printf("Desktop, starting reconstruction\n");
1402 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1403 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1405 /*
1406 GSList const * selection_objs = desktop->selection->list();
1407 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1409 }
1410 */
1411 desktop->selection->clear();
1413 // printf("Desktop, starting reconstruction end\n");
1414 }
1416 /// Called when document rebuild is finished.
1417 static void
1418 _reconstruction_finish (SPDesktop * desktop)
1419 {
1420 // printf("Desktop, finishing reconstruction\n");
1421 if (desktop->_reconstruction_old_layer_id == NULL)
1422 return;
1424 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1425 if (newLayer != NULL)
1426 desktop->setCurrentLayer(newLayer);
1428 g_free(desktop->_reconstruction_old_layer_id);
1429 desktop->_reconstruction_old_layer_id = NULL;
1430 // printf("Desktop, finishing reconstruction end\n");
1431 return;
1432 }
1434 /**
1435 * Namedview_modified callback.
1436 */
1437 static void
1438 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1439 {
1440 SPNamedView *nv=SP_NAMEDVIEW(obj);
1442 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1444 /* Recalculate snap distances */
1445 /* FIXME: why is the desktop getting involved in setting up something
1446 ** that is entirely to do with the namedview?
1447 */
1448 _update_snap_distances (desktop);
1450 /* Show/hide page background */
1451 if (nv->pagecolor & 0xff) {
1452 sp_canvas_item_show (desktop->table);
1453 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1454 sp_canvas_item_move_to_z (desktop->table, 0);
1455 } else {
1456 sp_canvas_item_hide (desktop->table);
1457 }
1459 /* Show/hide page border */
1460 if (nv->showborder) {
1461 // show
1462 sp_canvas_item_show (desktop->page_border);
1463 // set color and shadow
1464 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1465 if (nv->pageshadow) {
1466 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1467 }
1468 // place in the z-order stack
1469 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1470 sp_canvas_item_move_to_z (desktop->page_border, 2);
1471 } else {
1472 int order = sp_canvas_item_order (desktop->page_border);
1473 int morder = sp_canvas_item_order (desktop->drawing);
1474 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1475 morder - order);
1476 }
1477 } else {
1478 sp_canvas_item_hide (desktop->page_border);
1479 if (nv->pageshadow) {
1480 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1481 }
1482 }
1484 /* Show/hide page shadow */
1485 if (nv->showpageshadow && nv->pageshadow) {
1486 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1487 } else {
1488 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1489 }
1491 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1492 (SP_RGBA32_R_U(nv->pagecolor) +
1493 SP_RGBA32_G_U(nv->pagecolor) +
1494 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1495 // the background color is light or transparent, use black outline
1496 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1497 } else { // use white outline
1498 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1499 }
1500 }
1501 }
1503 /**
1504 * Callback to reset snapper's distances.
1505 */
1506 static void
1507 _update_snap_distances (SPDesktop *desktop)
1508 {
1509 SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1511 SPNamedView &nv = *desktop->namedview;
1513 //tell all grid snappers
1514 for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1515 Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1516 grid->snapper->setDistance(sp_convert_distance_full(nv.gridtolerance,
1517 *nv.gridtoleranceunit,
1518 px));
1519 }
1521 nv.snap_manager.guide.setDistance(sp_convert_distance_full(nv.guidetolerance,
1522 *nv.guidetoleranceunit,
1523 px));
1524 nv.snap_manager.object.setDistance(sp_convert_distance_full(nv.objecttolerance,
1525 *nv.objecttoleranceunit,
1526 px));
1527 }
1530 NR::Matrix SPDesktop::w2d() const
1531 {
1532 return _w2d;
1533 }
1535 NR::Point SPDesktop::w2d(NR::Point const &p) const
1536 {
1537 return p * _w2d;
1538 }
1540 NR::Point SPDesktop::d2w(NR::Point const &p) const
1541 {
1542 return p * _d2w;
1543 }
1545 NR::Matrix SPDesktop::doc2dt() const
1546 {
1547 return _doc2dt;
1548 }
1550 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1551 {
1552 return p * _doc2dt;
1553 }
1555 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1556 {
1557 return p / _doc2dt;
1558 }
1561 /**
1562 * Pop event context from desktop's context stack. Never used.
1563 */
1564 // void
1565 // SPDesktop::pop_event_context (unsigned int key)
1566 // {
1567 // SPEventContext *ec = NULL;
1568 //
1569 // if (event_context && event_context->key == key) {
1570 // g_return_if_fail (event_context);
1571 // g_return_if_fail (event_context->next);
1572 // ec = event_context;
1573 // sp_event_context_deactivate (ec);
1574 // event_context = ec->next;
1575 // sp_event_context_activate (event_context);
1576 // _event_context_changed_signal.emit (this, ec);
1577 // }
1578 //
1579 // SPEventContext *ref = event_context;
1580 // while (ref && ref->next && ref->next->key != key)
1581 // ref = ref->next;
1582 //
1583 // if (ref && ref->next) {
1584 // ec = ref->next;
1585 // ref->next = ec->next;
1586 // }
1587 //
1588 // if (ec) {
1589 // sp_event_context_finish (ec);
1590 // g_object_unref (G_OBJECT (ec));
1591 // }
1592 // }
1594 /*
1595 Local Variables:
1596 mode:c++
1597 c-file-style:"stroustrup"
1598 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1599 indent-tabs-mode:nil
1600 fill-column:99
1601 End:
1602 */
1603 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :