9498d539910940307901fb5ac8177430850cdf5f
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( false )
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 = &Inkscape::UI::Dialog::DialogManager::getInstance();
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 showGrids(namedview->grids_visible, false);
312 }
315 void SPDesktop::destroy()
316 {
317 namedview->hide(this);
319 _activate_connection.disconnect();
320 _deactivate_connection.disconnect();
321 _sel_modified_connection.disconnect();
322 _sel_changed_connection.disconnect();
323 _modified_connection.disconnect();
324 _commit_connection.disconnect();
325 _reconstruction_start_connection.disconnect();
326 _reconstruction_finish_connection.disconnect();
328 g_signal_handlers_disconnect_by_func(G_OBJECT (acetate), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
329 g_signal_handlers_disconnect_by_func(G_OBJECT (main), (gpointer) G_CALLBACK(sp_desktop_root_handler), this);
330 g_signal_handlers_disconnect_by_func(G_OBJECT (drawing), (gpointer) G_CALLBACK(_arena_handler), this);
332 while (event_context) {
333 SPEventContext *ec = event_context;
334 event_context = ec->next;
335 sp_event_context_finish (ec);
336 g_object_unref (G_OBJECT (ec));
337 }
339 if (_layer_hierarchy) {
340 delete _layer_hierarchy;
341 // _layer_hierarchy = NULL; //this should be here, but commented to find other bug somewhere else.
342 }
344 if (layer_manager) {
345 delete layer_manager;
346 layer_manager = NULL;
347 }
349 if (_inkscape) {
350 _inkscape = NULL;
351 }
353 if (drawing) {
354 sp_item_invoke_hide (SP_ITEM (sp_document_root (doc())), dkey);
355 drawing = NULL;
356 }
358 delete _guides_message_context;
359 _guides_message_context = NULL;
361 g_list_free (zooms_past);
362 g_list_free (zooms_future);
363 }
365 SPDesktop::~SPDesktop() {}
367 //--------------------------------------------------------------------
368 /* Public methods */
370 void SPDesktop::setDisplayModeNormal()
371 {
372 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_NORMAL;
373 canvas->rendermode = RENDERMODE_NORMAL; // canvas needs that for choosing the best buffer size
374 displayMode = RENDERMODE_NORMAL;
375 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
376 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
377 }
379 void SPDesktop::setDisplayModeOutline()
380 {
381 SP_CANVAS_ARENA (drawing)->arena->rendermode = RENDERMODE_OUTLINE;
382 canvas->rendermode = RENDERMODE_OUTLINE; // canvas needs that for choosing the best buffer size
383 displayMode = RENDERMODE_OUTLINE;
384 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw
385 _widget->setTitle(SP_DOCUMENT_NAME(sp_desktop_document(this)));
386 }
388 void SPDesktop::displayModeToggle()
389 {
390 if (displayMode == RENDERMODE_OUTLINE)
391 setDisplayModeNormal();
392 else
393 setDisplayModeOutline();
394 }
396 /**
397 * Returns current root (=bottom) layer.
398 */
399 SPObject *SPDesktop::currentRoot() const
400 {
401 return _layer_hierarchy ? _layer_hierarchy->top() : NULL;
402 }
404 /**
405 * Returns current top layer.
406 */
407 SPObject *SPDesktop::currentLayer() const
408 {
409 return _layer_hierarchy ? _layer_hierarchy->bottom() : NULL;
410 }
412 /**
413 * Sets the current layer of the desktop.
414 *
415 * Make \a object the top layer.
416 */
417 void SPDesktop::setCurrentLayer(SPObject *object) {
418 g_return_if_fail(SP_IS_GROUP(object));
419 g_return_if_fail( currentRoot() == object || (currentRoot() && currentRoot()->isAncestorOf(object)) );
420 // printf("Set Layer to ID: %s\n", SP_OBJECT_ID(object));
421 _layer_hierarchy->setBottom(object);
422 }
424 /**
425 * Return layer that contains \a object.
426 */
427 SPObject *SPDesktop::layerForObject(SPObject *object) {
428 g_return_val_if_fail(object != NULL, NULL);
430 SPObject *root=currentRoot();
431 object = SP_OBJECT_PARENT(object);
432 while ( object && object != root && !isLayer(object) ) {
433 object = SP_OBJECT_PARENT(object);
434 }
435 return object;
436 }
438 /**
439 * True if object is a layer.
440 */
441 bool SPDesktop::isLayer(SPObject *object) const {
442 return ( SP_IS_GROUP(object)
443 && ( SP_GROUP(object)->effectiveLayerMode(this->dkey)
444 == SPGroup::LAYER ) );
445 }
447 /**
448 * True if desktop viewport fully contains \a item's bbox.
449 */
450 bool SPDesktop::isWithinViewport (SPItem *item) const
451 {
452 NR::Rect const viewport = get_display_area();
453 NR::Maybe<NR::Rect> const bbox = sp_item_bbox_desktop(item);
454 if (bbox) {
455 return viewport.contains(*bbox);
456 } else {
457 return true;
458 }
459 }
461 ///
462 bool SPDesktop::itemIsHidden(SPItem const *item) const {
463 return item->isHidden(this->dkey);
464 }
466 /**
467 * Set activate property of desktop; emit signal if changed.
468 */
469 void
470 SPDesktop::set_active (bool new_active)
471 {
472 if (new_active != _active) {
473 _active = new_active;
474 if (new_active) {
475 _activate_signal.emit();
476 } else {
477 _deactivate_signal.emit();
478 }
479 }
480 }
482 /**
483 * Set activate status of current desktop's named view.
484 */
485 void
486 SPDesktop::activate_guides(bool activate)
487 {
488 guides_active = activate;
489 namedview->activateGuides(this, activate);
490 }
492 /**
493 * Make desktop switch documents.
494 */
495 void
496 SPDesktop::change_document (SPDocument *theDocument)
497 {
498 g_return_if_fail (theDocument != NULL);
500 /* unselect everything before switching documents */
501 selection->clear();
503 setDocument (theDocument);
504 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
505 _document_replaced_signal.emit (this, theDocument);
506 }
508 /**
509 * Make desktop switch event contexts.
510 */
511 void
512 SPDesktop::set_event_context (GtkType type, const gchar *config)
513 {
514 SPEventContext *ec;
515 while (event_context) {
516 ec = event_context;
517 sp_event_context_deactivate (ec);
518 event_context = ec->next;
519 sp_event_context_finish (ec);
520 g_object_unref (G_OBJECT (ec));
521 }
523 Inkscape::XML::Node *repr = (config) ? inkscape_get_repr (_inkscape, config) : NULL;
524 ec = sp_event_context_new (type, this, repr, SP_EVENT_CONTEXT_STATIC);
525 ec->next = event_context;
526 event_context = ec;
527 sp_event_context_activate (ec);
528 _event_context_changed_signal.emit (this, ec);
529 }
531 /**
532 * Push event context onto desktop's context stack.
533 */
534 void
535 SPDesktop::push_event_context (GtkType type, const gchar *config, unsigned int key)
536 {
537 SPEventContext *ref, *ec;
538 Inkscape::XML::Node *repr;
540 if (event_context && event_context->key == key) return;
541 ref = event_context;
542 while (ref && ref->next && ref->next->key != key) ref = ref->next;
543 if (ref && ref->next) {
544 ec = ref->next;
545 ref->next = ec->next;
546 sp_event_context_finish (ec);
547 g_object_unref (G_OBJECT (ec));
548 }
550 if (event_context) sp_event_context_deactivate (event_context);
551 repr = (config) ? inkscape_get_repr (INKSCAPE, config) : NULL;
552 ec = sp_event_context_new (type, this, repr, key);
553 ec->next = event_context;
554 event_context = ec;
555 sp_event_context_activate (ec);
556 _event_context_changed_signal.emit (this, ec);
557 }
559 /**
560 * Sets the coordinate status to a given point
561 */
562 void
563 SPDesktop::set_coordinate_status (NR::Point p) {
564 _widget->setCoordinateStatus(p);
565 }
567 /**
568 * \see sp_document_item_from_list_at_point_bottom()
569 */
570 SPItem *
571 SPDesktop::item_from_list_at_point_bottom (const GSList *list, NR::Point const p) const
572 {
573 g_return_val_if_fail (doc() != NULL, NULL);
574 return sp_document_item_from_list_at_point_bottom (dkey, SP_GROUP (doc()->root), list, p);
575 }
577 /**
578 * \see sp_document_item_at_point()
579 */
580 SPItem *
581 SPDesktop::item_at_point (NR::Point const p, bool into_groups, SPItem *upto) const
582 {
583 g_return_val_if_fail (doc() != NULL, NULL);
584 return sp_document_item_at_point (doc(), dkey, p, into_groups, upto);
585 }
587 /**
588 * \see sp_document_group_at_point()
589 */
590 SPItem *
591 SPDesktop::group_at_point (NR::Point const p) const
592 {
593 g_return_val_if_fail (doc() != NULL, NULL);
594 return sp_document_group_at_point (doc(), dkey, p);
595 }
597 /**
598 * \brief Returns the mouse point in document coordinates; if mouse is
599 * outside the canvas, returns the center of canvas viewpoint
600 */
601 NR::Point
602 SPDesktop::point() const
603 {
604 NR::Point p = _widget->getPointer();
605 NR::Point pw = sp_canvas_window_to_world (canvas, p);
606 p = w2d(pw);
608 NR::Rect const r = canvas->getViewbox();
610 NR::Point r0 = w2d(r.min());
611 NR::Point r1 = w2d(r.max());
613 if (p[NR::X] >= r0[NR::X] &&
614 p[NR::X] <= r1[NR::X] &&
615 p[NR::Y] >= r1[NR::Y] &&
616 p[NR::Y] <= r0[NR::Y])
617 {
618 return p;
619 } else {
620 return (r0 + r1) / 2;
621 }
622 }
624 /**
625 * Put current zoom data in history list.
626 */
627 void
628 SPDesktop::push_current_zoom (GList **history)
629 {
630 NR::Rect const area = get_display_area();
632 NRRect *old_zoom = g_new(NRRect, 1);
633 old_zoom->x0 = area.min()[NR::X];
634 old_zoom->x1 = area.max()[NR::X];
635 old_zoom->y0 = area.min()[NR::Y];
636 old_zoom->y1 = area.max()[NR::Y];
637 if ( *history == NULL
638 || !( ( ((NRRect *) ((*history)->data))->x0 == old_zoom->x0 ) &&
639 ( ((NRRect *) ((*history)->data))->x1 == old_zoom->x1 ) &&
640 ( ((NRRect *) ((*history)->data))->y0 == old_zoom->y0 ) &&
641 ( ((NRRect *) ((*history)->data))->y1 == old_zoom->y1 ) ) )
642 {
643 *history = g_list_prepend (*history, old_zoom);
644 }
645 }
647 /**
648 * Set viewbox.
649 */
650 void
651 SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log)
652 {
653 g_assert(_widget);
655 // save the zoom
656 if (log) {
657 push_current_zoom(&zooms_past);
658 // if we do a logged zoom, our zoom-forward list is invalidated, so delete it
659 g_list_free (zooms_future);
660 zooms_future = NULL;
661 }
663 double const cx = 0.5 * (x0 + x1);
664 double const cy = 0.5 * (y0 + y1);
666 NR::Rect const viewbox = NR::expand(canvas->getViewbox(), border);
668 double scale = expansion(_d2w);
669 double newscale;
670 if (((x1 - x0) * viewbox.dimensions()[NR::Y]) > ((y1 - y0) * viewbox.dimensions()[NR::X])) {
671 newscale = viewbox.dimensions()[NR::X] / (x1 - x0);
672 } else {
673 newscale = viewbox.dimensions()[NR::Y] / (y1 - y0);
674 }
676 newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
678 int clear = FALSE;
679 if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) {
680 /* Set zoom factors */
681 _d2w = NR::Matrix(NR::scale(newscale, -newscale));
682 _w2d = NR::Matrix(NR::scale(1/newscale, 1/-newscale));
683 sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w);
684 clear = TRUE;
685 }
687 /* Calculate top left corner */
688 x0 = cx - 0.5 * viewbox.dimensions()[NR::X] / newscale;
689 y1 = cy + 0.5 * viewbox.dimensions()[NR::Y] / newscale;
691 /* Scroll */
692 sp_canvas_scroll_to (canvas, x0 * newscale - border, y1 * -newscale - border, clear);
694 _widget->updateRulers();
695 _widget->updateScrollbars(expansion(_d2w));
696 _widget->updateZoom();
697 }
699 void SPDesktop::set_display_area(NR::Rect const &a, NR::Coord b, bool log)
700 {
701 set_display_area(a.min()[NR::X], a.min()[NR::Y], a.max()[NR::X], a.max()[NR::Y], b, log);
702 }
704 /**
705 * Return viewbox dimensions.
706 */
707 NR::Rect SPDesktop::get_display_area() const
708 {
709 NR::Rect const viewbox = canvas->getViewbox();
711 double const scale = _d2w[0];
713 return NR::Rect(NR::Point(viewbox.min()[NR::X] / scale, viewbox.max()[NR::Y] / -scale),
714 NR::Point(viewbox.max()[NR::X] / scale, viewbox.min()[NR::Y] / -scale));
715 }
717 /**
718 * Revert back to previous zoom if possible.
719 */
720 void
721 SPDesktop::prev_zoom()
722 {
723 if (zooms_past == NULL) {
724 messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom."));
725 return;
726 }
728 // push current zoom into forward zooms list
729 push_current_zoom (&zooms_future);
731 // restore previous zoom
732 set_display_area (((NRRect *) zooms_past->data)->x0,
733 ((NRRect *) zooms_past->data)->y0,
734 ((NRRect *) zooms_past->data)->x1,
735 ((NRRect *) zooms_past->data)->y1,
736 0, false);
738 // remove the just-added zoom from the past zooms list
739 zooms_past = g_list_remove (zooms_past, ((NRRect *) zooms_past->data));
740 }
742 /**
743 * Set zoom to next in list.
744 */
745 void
746 SPDesktop::next_zoom()
747 {
748 if (zooms_future == NULL) {
749 this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom."));
750 return;
751 }
753 // push current zoom into past zooms list
754 push_current_zoom (&zooms_past);
756 // restore next zoom
757 set_display_area (((NRRect *) zooms_future->data)->x0,
758 ((NRRect *) zooms_future->data)->y0,
759 ((NRRect *) zooms_future->data)->x1,
760 ((NRRect *) zooms_future->data)->y1,
761 0, false);
763 // remove the just-used zoom from the zooms_future list
764 zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data));
765 }
767 /**
768 * Zoom to point with absolute zoom factor.
769 */
770 void
771 SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom)
772 {
773 zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX);
775 // maximum or minimum zoom reached, but there's no exact equality because of rounding errors;
776 // this check prevents "sliding" when trying to zoom in at maximum zoom;
777 /// \todo someone please fix calculations properly and remove this hack
778 if (fabs(expansion(_d2w) - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001))
779 return;
781 NR::Rect const viewbox = canvas->getViewbox();
783 double const width2 = viewbox.dimensions()[NR::X] / zoom;
784 double const height2 = viewbox.dimensions()[NR::Y] / zoom;
786 set_display_area(cx - px * width2,
787 cy - py * height2,
788 cx + (1 - px) * width2,
789 cy + (1 - py) * height2,
790 0.0);
791 }
793 /**
794 * Zoom to center with absolute zoom factor.
795 */
796 void
797 SPDesktop::zoom_absolute (double cx, double cy, double zoom)
798 {
799 zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom);
800 }
802 /**
803 * Zoom to point with relative zoom factor.
804 */
805 void
806 SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom)
807 {
808 NR::Rect const area = get_display_area();
810 if (cx < area.min()[NR::X]) {
811 cx = area.min()[NR::X];
812 }
813 if (cx > area.max()[NR::X]) {
814 cx = area.max()[NR::X];
815 }
816 if (cy < area.min()[NR::Y]) {
817 cy = area.min()[NR::Y];
818 }
819 if (cy > area.max()[NR::Y]) {
820 cy = area.max()[NR::Y];
821 }
823 gdouble const scale = expansion(_d2w) * zoom;
824 double const px = (cx - area.min()[NR::X]) / area.dimensions()[NR::X];
825 double const py = (cy - area.min()[NR::Y]) / area.dimensions()[NR::Y];
827 zoom_absolute_keep_point(cx, cy, px, py, scale);
828 }
830 /**
831 * Zoom to center with relative zoom factor.
832 */
833 void
834 SPDesktop::zoom_relative (double cx, double cy, double zoom)
835 {
836 gdouble scale = expansion(_d2w) * zoom;
837 zoom_absolute (cx, cy, scale);
838 }
840 /**
841 * Set display area to origin and current document dimensions.
842 */
843 void
844 SPDesktop::zoom_page()
845 {
846 NR::Rect d(NR::Point(0, 0),
847 NR::Point(sp_document_width(doc()), sp_document_height(doc())));
849 if (d.isEmpty(1.0)) {
850 return;
851 }
853 set_display_area(d, 10);
854 }
856 /**
857 * Set display area to current document width.
858 */
859 void
860 SPDesktop::zoom_page_width()
861 {
862 NR::Rect const a = get_display_area();
864 if (sp_document_width(doc()) < 1.0) {
865 return;
866 }
868 NR::Rect d(NR::Point(0, a.midpoint()[NR::Y]),
869 NR::Point(sp_document_width(doc()), a.midpoint()[NR::Y]));
871 set_display_area(d, 10);
872 }
874 /**
875 * Zoom to selection.
876 */
877 void
878 SPDesktop::zoom_selection()
879 {
880 NR::Maybe<NR::Rect> const d = selection->bounds();
882 if ( !d || d->isEmpty(0.1) ) {
883 return;
884 }
886 set_display_area(*d, 10);
887 }
889 /**
890 * Tell widget to let zoom widget grab keyboard focus.
891 */
892 void
893 SPDesktop::zoom_grab_focus()
894 {
895 _widget->letZoomGrabFocus();
896 }
898 /**
899 * Zoom to whole drawing.
900 */
901 void
902 SPDesktop::zoom_drawing()
903 {
904 g_return_if_fail (doc() != NULL);
905 SPItem *docitem = SP_ITEM (sp_document_root (doc()));
906 g_return_if_fail (docitem != NULL);
908 NR::Maybe<NR::Rect> d = sp_item_bbox_desktop(docitem);
910 /* Note that the second condition here indicates that
911 ** there are no items in the drawing.
912 */
913 if ( !d || d->isEmpty(1.0) ) {
914 return;
915 }
917 set_display_area(*d, 10);
918 }
920 /**
921 * Scroll canvas by specific coordinate amount.
922 */
923 void
924 SPDesktop::scroll_world (double dx, double dy, bool is_scrolling)
925 {
926 g_assert(_widget);
928 NR::Rect const viewbox = canvas->getViewbox();
930 sp_canvas_scroll_to(canvas, viewbox.min()[NR::X] - dx, viewbox.min()[NR::Y] - dy, FALSE, is_scrolling);
932 _widget->updateRulers();
933 _widget->updateScrollbars(expansion(_d2w));
934 }
936 bool
937 SPDesktop::scroll_to_point (NR::Point const *p, gdouble autoscrollspeed)
938 {
939 gdouble autoscrolldistance = (gdouble) prefs_get_int_attribute_limited ("options.autoscrolldistance", "value", 0, -1000, 10000);
941 // autoscrolldistance is in screen pixels, but the display area is in document units
942 autoscrolldistance /= expansion(_d2w);
943 NR::Rect const dbox = NR::expand(get_display_area(), -autoscrolldistance);
945 if (!((*p)[NR::X] > dbox.min()[NR::X] && (*p)[NR::X] < dbox.max()[NR::X]) ||
946 !((*p)[NR::Y] > dbox.min()[NR::Y] && (*p)[NR::Y] < dbox.max()[NR::Y]) ) {
948 NR::Point const s_w( (*p) * _d2w );
950 gdouble x_to;
951 if ((*p)[NR::X] < dbox.min()[NR::X])
952 x_to = dbox.min()[NR::X];
953 else if ((*p)[NR::X] > dbox.max()[NR::X])
954 x_to = dbox.max()[NR::X];
955 else
956 x_to = (*p)[NR::X];
958 gdouble y_to;
959 if ((*p)[NR::Y] < dbox.min()[NR::Y])
960 y_to = dbox.min()[NR::Y];
961 else if ((*p)[NR::Y] > dbox.max()[NR::Y])
962 y_to = dbox.max()[NR::Y];
963 else
964 y_to = (*p)[NR::Y];
966 NR::Point const d_dt(x_to, y_to);
967 NR::Point const d_w( d_dt * _d2w );
968 NR::Point const moved_w( d_w - s_w );
970 if (autoscrollspeed == 0)
971 autoscrollspeed = prefs_get_double_attribute_limited ("options.autoscrollspeed", "value", 1, 0, 10);
973 if (autoscrollspeed != 0)
974 scroll_world (autoscrollspeed * moved_w);
976 return true;
977 }
978 return false;
979 }
981 bool
982 SPDesktop::is_iconified()
983 {
984 return 0!=(window_state & GDK_WINDOW_STATE_ICONIFIED);
985 }
987 void
988 SPDesktop::iconify()
989 {
990 _widget->setIconified();
991 }
993 bool
994 SPDesktop::is_maximized()
995 {
996 return 0!=(window_state & GDK_WINDOW_STATE_MAXIMIZED);
997 }
999 void
1000 SPDesktop::maximize()
1001 {
1002 _widget->setMaximized();
1003 }
1005 bool
1006 SPDesktop::is_fullscreen()
1007 {
1008 return 0!=(window_state & GDK_WINDOW_STATE_FULLSCREEN);
1009 }
1011 void
1012 SPDesktop::fullscreen()
1013 {
1014 _widget->setFullscreen();
1015 }
1017 void
1018 SPDesktop::getWindowGeometry (gint &x, gint &y, gint &w, gint &h)
1019 {
1020 _widget->getGeometry (x, y, w, h);
1021 }
1023 void
1024 SPDesktop::setWindowPosition (NR::Point p)
1025 {
1026 _widget->setPosition (p);
1027 }
1029 void
1030 SPDesktop::setWindowSize (gint w, gint h)
1031 {
1032 _widget->setSize (w, h);
1033 }
1035 void
1036 SPDesktop::setWindowTransient (void *p, int transient_policy)
1037 {
1038 _widget->setTransient (p, transient_policy);
1039 }
1041 Gtk::Window*
1042 SPDesktop::getToplevel( )
1043 {
1044 return _widget->getWindow();
1045 }
1047 void
1048 SPDesktop::presentWindow()
1049 {
1050 _widget->present();
1051 }
1053 bool
1054 SPDesktop::warnDialog (gchar *text)
1055 {
1056 return _widget->warnDialog (text);
1057 }
1059 void
1060 SPDesktop::toggleRulers()
1061 {
1062 _widget->toggleRulers();
1063 }
1065 void
1066 SPDesktop::toggleScrollbars()
1067 {
1068 _widget->toggleScrollbars();
1069 }
1071 void
1072 SPDesktop::layoutWidget()
1073 {
1074 _widget->layout();
1075 }
1077 void
1078 SPDesktop::destroyWidget()
1079 {
1080 _widget->destroy();
1081 }
1083 bool
1084 SPDesktop::shutdown()
1085 {
1086 return _widget->shutdown();
1087 }
1089 bool SPDesktop::onDeleteUI (GdkEventAny*)
1090 {
1091 if(shutdown())
1092 return true;
1094 destroyWidget();
1095 return false;
1096 }
1098 /**
1099 * onWindowStateEvent
1100 *
1101 * Called when the window changes its maximize/fullscreen/iconify/pinned state.
1102 * Since GTK doesn't have a way to query this state information directly, we
1103 * record it for the desktop here, and also possibly trigger a layout.
1104 */
1105 bool
1106 SPDesktop::onWindowStateEvent (GdkEventWindowState* event)
1107 {
1108 // Record the desktop window's state
1109 window_state = event->new_window_state;
1111 // Layout may differ depending on full-screen mode or not
1112 GdkWindowState changed = event->changed_mask;
1113 if (changed & (GDK_WINDOW_STATE_FULLSCREEN|GDK_WINDOW_STATE_MAXIMIZED)) {
1114 layoutWidget();
1115 }
1117 return false;
1118 }
1120 void
1121 SPDesktop::setToolboxFocusTo (gchar const *label)
1122 {
1123 _widget->setToolboxFocusTo (label);
1124 }
1126 void
1127 SPDesktop::setToolboxAdjustmentValue (gchar const* id, double val)
1128 {
1129 _widget->setToolboxAdjustmentValue (id, val);
1130 }
1132 void
1133 SPDesktop::setToolboxSelectOneValue (gchar const* id, gint val)
1134 {
1135 _widget->setToolboxSelectOneValue (id, val);
1136 }
1138 bool
1139 SPDesktop::isToolboxButtonActive (gchar const *id)
1140 {
1141 return _widget->isToolboxButtonActive (id);
1142 }
1144 void
1145 SPDesktop::emitToolSubselectionChanged(gpointer data)
1146 {
1147 _tool_subselection_changed.emit(data);
1148 inkscape_subselection_changed (this);
1149 }
1151 void
1152 SPDesktop::updateNow()
1153 {
1154 sp_canvas_update_now(canvas);
1155 }
1157 void
1158 SPDesktop::enableInteraction()
1159 {
1160 _widget->enableInteraction();
1161 }
1163 void SPDesktop::disableInteraction()
1164 {
1165 _widget->disableInteraction();
1166 }
1168 void SPDesktop::setWaitingCursor()
1169 {
1170 GdkCursor *waiting = gdk_cursor_new(GDK_WATCH);
1171 gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(this))->window, waiting);
1172 gdk_cursor_unref(waiting);
1173 waiting_cursor = true;
1175 // Stupidly broken GDK cannot just set the new cursor right now - it needs some main loop iterations for that
1176 // Since setting waiting_cursor is usually immediately followed by some Real Work, we must run the iterations here
1177 // CAUTION: iterations may redraw, and redraw may be interrupted, so you cannot assume that anything is the same
1178 // after the call to setWaitingCursor as it was before
1179 while( Gtk::Main::events_pending() )
1180 Gtk::Main::iteration();
1181 }
1183 void SPDesktop::clearWaitingCursor()
1184 {
1185 if (waiting_cursor)
1186 sp_event_context_update_cursor(sp_desktop_event_context(this));
1187 }
1189 void SPDesktop::toggleColorProfAdjust()
1190 {
1191 _widget->toggleColorProfAdjust();
1192 }
1194 void SPDesktop::toggleGrids()
1195 {
1196 if (namedview->grids) {
1197 if(gridgroup) {
1198 showGrids(!grids_visible);
1199 }
1200 } else {
1201 //there is no grid present at the moment. add a rectangular grid and make it visible
1202 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1203 Inkscape::CanvasGrid::writeNewGridToRepr(repr, sp_desktop_document(this), Inkscape::GRID_RECTANGULAR);
1204 showGrids(true);
1205 }
1206 }
1208 void SPDesktop::showGrids(bool show, bool dirty_document)
1209 {
1210 grids_visible = show;
1211 sp_namedview_show_grids(namedview, grids_visible, dirty_document);
1212 if (show) {
1213 sp_canvas_item_show(SP_CANVAS_ITEM(gridgroup));
1214 } else {
1215 sp_canvas_item_hide(SP_CANVAS_ITEM(gridgroup));
1216 }
1217 }
1219 void SPDesktop::toggleSnapping()
1220 {
1221 bool v = namedview->snap_manager.getSnapEnabledGlobally();
1222 Inkscape::XML::Node *repr = SP_OBJECT_REPR(namedview);
1223 sp_repr_set_boolean(repr, "inkscape:snap-global", !v);
1224 }
1226 //----------------------------------------------------------------------
1227 // Callback implementations. The virtual ones are connected by the view.
1229 void
1230 SPDesktop::onPositionSet (double x, double y)
1231 {
1232 _widget->viewSetPosition (NR::Point(x,y));
1233 }
1235 void
1236 SPDesktop::onResized (double /*x*/, double /*y*/)
1237 {
1238 // Nothing called here
1239 }
1241 /**
1242 * Redraw callback; queues Gtk redraw; connected by View.
1243 */
1244 void
1245 SPDesktop::onRedrawRequested ()
1246 {
1247 if (main) {
1248 _widget->requestCanvasUpdate();
1249 }
1250 }
1252 void
1253 SPDesktop::updateCanvasNow()
1254 {
1255 _widget->requestCanvasUpdateAndWait();
1256 }
1258 /**
1259 * Associate document with desktop.
1260 */
1261 void
1262 SPDesktop::setDocument (SPDocument *doc)
1263 {
1264 if (this->doc() && doc) {
1265 namedview->hide(this);
1266 sp_item_invoke_hide (SP_ITEM (sp_document_root (this->doc())), dkey);
1267 }
1269 if (_layer_hierarchy) {
1270 _layer_hierarchy->clear();
1271 delete _layer_hierarchy;
1272 }
1273 _layer_hierarchy = new Inkscape::ObjectHierarchy(NULL);
1274 _layer_hierarchy->connectAdded(sigc::bind(sigc::ptr_fun(_layer_activated), this));
1275 _layer_hierarchy->connectRemoved(sigc::bind(sigc::ptr_fun(_layer_deactivated), this));
1276 _layer_hierarchy->connectChanged(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this));
1277 _layer_hierarchy->setTop(SP_DOCUMENT_ROOT(doc));
1279 /* setup EventLog */
1280 event_log = new Inkscape::EventLog(doc);
1281 doc->addUndoObserver(*event_log);
1283 _commit_connection.disconnect();
1284 _commit_connection = doc->connectCommit(sigc::mem_fun(*this, &SPDesktop::updateNow));
1286 /// \todo fixme: This condition exists to make sure the code
1287 /// inside is NOT called on initialization, only on replacement. But there
1288 /// are surely more safe methods to accomplish this.
1289 // TODO since the comment had reversed logic, check the intent of this block of code:
1290 if (drawing) {
1291 NRArenaItem *ai = 0;
1293 namedview = sp_document_namedview (doc, NULL);
1294 _modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&_namedview_modified), this));
1295 number = namedview->getViewCount();
1297 ai = sp_item_invoke_show (SP_ITEM (sp_document_root (doc)),
1298 SP_CANVAS_ARENA (drawing)->arena,
1299 dkey,
1300 SP_ITEM_SHOW_DISPLAY);
1301 if (ai) {
1302 nr_arena_item_add_child (SP_CANVAS_ARENA (drawing)->root, ai, NULL);
1303 nr_arena_item_unref (ai);
1304 }
1305 namedview->show(this);
1306 /* Ugly hack */
1307 activate_guides (true);
1308 /* Ugly hack */
1309 _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this);
1310 }
1312 _document_replaced_signal.emit (this, doc);
1314 View::setDocument (doc);
1315 }
1317 void
1318 SPDesktop::onStatusMessage
1319 (Inkscape::MessageType type, gchar const *message)
1320 {
1321 if (_widget) {
1322 _widget->setMessage(type, message);
1323 }
1324 }
1326 void
1327 SPDesktop::onDocumentURISet (gchar const* uri)
1328 {
1329 _widget->setTitle(uri);
1330 }
1332 /**
1333 * Resized callback.
1334 */
1335 void
1336 SPDesktop::onDocumentResized (gdouble width, gdouble height)
1337 {
1338 _doc2dt[5] = height;
1339 sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (drawing), _doc2dt);
1340 NR::Rect const a(NR::Point(0, 0), NR::Point(width, height));
1341 SP_CTRLRECT(page)->setRectangle(a);
1342 SP_CTRLRECT(page_border)->setRectangle(a);
1343 }
1346 void
1347 SPDesktop::_onActivate (SPDesktop* dt)
1348 {
1349 if (!dt->_widget) return;
1350 dt->_widget->activateDesktop();
1351 }
1353 void
1354 SPDesktop::_onDeactivate (SPDesktop* dt)
1355 {
1356 if (!dt->_widget) return;
1357 dt->_widget->deactivateDesktop();
1358 }
1360 void
1361 SPDesktop::_onSelectionModified
1362 (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt)
1363 {
1364 if (!dt->_widget) return;
1365 dt->_widget->updateScrollbars (expansion(dt->_d2w));
1366 }
1368 static void
1369 _onSelectionChanged
1370 (Inkscape::Selection *selection, SPDesktop *desktop)
1371 {
1372 /** \todo
1373 * only change the layer for single selections, or what?
1374 * This seems reasonable -- for multiple selections there can be many
1375 * different layers involved.
1376 */
1377 SPItem *item=selection->singleItem();
1378 if (item) {
1379 SPObject *layer=desktop->layerForObject(item);
1380 if ( layer && layer != desktop->currentLayer() ) {
1381 desktop->setCurrentLayer(layer);
1382 }
1383 }
1384 }
1386 /**
1387 * Calls event handler of current event context.
1388 * \param arena Unused
1389 * \todo fixme
1390 */
1391 static gint
1392 _arena_handler (SPCanvasArena */*arena*/, NRArenaItem *ai, GdkEvent *event, SPDesktop *desktop)
1393 {
1394 if (ai) {
1395 SPItem *spi = (SPItem*)NR_ARENA_ITEM_GET_DATA (ai);
1396 return sp_event_context_item_handler (desktop->event_context, spi, event);
1397 } else {
1398 return sp_event_context_root_handler (desktop->event_context, event);
1399 }
1400 }
1402 static void
1403 _layer_activated(SPObject *layer, SPDesktop *desktop) {
1404 g_return_if_fail(SP_IS_GROUP(layer));
1405 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::LAYER);
1406 }
1408 /// Callback
1409 static void
1410 _layer_deactivated(SPObject *layer, SPDesktop *desktop) {
1411 g_return_if_fail(SP_IS_GROUP(layer));
1412 SP_GROUP(layer)->setLayerDisplayMode(desktop->dkey, SPGroup::GROUP);
1413 }
1415 /// Callback
1416 static void
1417 _layer_hierarchy_changed(SPObject */*top*/, SPObject *bottom,
1418 SPDesktop *desktop)
1419 {
1420 desktop->_layer_changed_signal.emit (bottom);
1421 }
1423 /// Called when document is starting to be rebuilt.
1424 static void
1425 _reconstruction_start (SPDesktop * desktop)
1426 {
1427 // printf("Desktop, starting reconstruction\n");
1428 desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer()));
1429 desktop->_layer_hierarchy->setBottom(desktop->currentRoot());
1431 /*
1432 GSList const * selection_objs = desktop->selection->list();
1433 for (; selection_objs != NULL; selection_objs = selection_objs->next) {
1435 }
1436 */
1437 desktop->selection->clear();
1439 // printf("Desktop, starting reconstruction end\n");
1440 }
1442 /// Called when document rebuild is finished.
1443 static void
1444 _reconstruction_finish (SPDesktop * desktop)
1445 {
1446 // printf("Desktop, finishing reconstruction\n");
1447 if (desktop->_reconstruction_old_layer_id == NULL)
1448 return;
1450 SPObject * newLayer = SP_OBJECT_DOCUMENT(desktop->namedview)->getObjectById(desktop->_reconstruction_old_layer_id);
1451 if (newLayer != NULL)
1452 desktop->setCurrentLayer(newLayer);
1454 g_free(desktop->_reconstruction_old_layer_id);
1455 desktop->_reconstruction_old_layer_id = NULL;
1456 // printf("Desktop, finishing reconstruction end\n");
1457 return;
1458 }
1460 /**
1461 * Namedview_modified callback.
1462 */
1463 static void
1464 _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop)
1465 {
1466 SPNamedView *nv=SP_NAMEDVIEW(obj);
1468 if (flags & SP_OBJECT_MODIFIED_FLAG) {
1470 /* Recalculate snap distances */
1471 /* FIXME: why is the desktop getting involved in setting up something
1472 ** that is entirely to do with the namedview?
1473 */
1474 _update_snap_distances (desktop);
1476 /* Show/hide page background */
1477 if (nv->pagecolor & 0xff) {
1478 sp_canvas_item_show (desktop->table);
1479 ((CtrlRect *) desktop->table)->setColor(0x00000000, true, nv->pagecolor);
1480 sp_canvas_item_move_to_z (desktop->table, 0);
1481 } else {
1482 sp_canvas_item_hide (desktop->table);
1483 }
1485 /* Show/hide page border */
1486 if (nv->showborder) {
1487 // show
1488 sp_canvas_item_show (desktop->page_border);
1489 // set color and shadow
1490 ((CtrlRect *) desktop->page_border)->setColor(nv->bordercolor, false, 0x00000000);
1491 if (nv->pageshadow) {
1492 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1493 }
1494 // place in the z-order stack
1495 if (nv->borderlayer == SP_BORDER_LAYER_BOTTOM) {
1496 sp_canvas_item_move_to_z (desktop->page_border, 2);
1497 } else {
1498 int order = sp_canvas_item_order (desktop->page_border);
1499 int morder = sp_canvas_item_order (desktop->drawing);
1500 if (morder > order) sp_canvas_item_raise (desktop->page_border,
1501 morder - order);
1502 }
1503 } else {
1504 sp_canvas_item_hide (desktop->page_border);
1505 if (nv->pageshadow) {
1506 ((CtrlRect *) desktop->page)->setShadow(0, 0x00000000);
1507 }
1508 }
1510 /* Show/hide page shadow */
1511 if (nv->showpageshadow && nv->pageshadow) {
1512 ((CtrlRect *) desktop->page_border)->setShadow(nv->pageshadow, nv->bordercolor);
1513 } else {
1514 ((CtrlRect *) desktop->page_border)->setShadow(0, 0x00000000);
1515 }
1517 if (SP_RGBA32_A_U(nv->pagecolor) < 128 ||
1518 (SP_RGBA32_R_U(nv->pagecolor) +
1519 SP_RGBA32_G_U(nv->pagecolor) +
1520 SP_RGBA32_B_U(nv->pagecolor)) >= 384) {
1521 // the background color is light or transparent, use black outline
1522 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "onlight", 0xff);
1523 } else { // use white outline
1524 SP_CANVAS_ARENA (desktop->drawing)->arena->outlinecolor = prefs_get_int_attribute("options.wireframecolors", "ondark", 0xffffffff);
1525 }
1526 }
1527 }
1529 /**
1530 * Callback to reset snapper's distances.
1531 */
1532 static void
1533 _update_snap_distances (SPDesktop *desktop)
1534 {
1535 SPUnit const &px = sp_unit_get_by_id(SP_UNIT_PX);
1537 SPNamedView &nv = *desktop->namedview;
1539 //tell all grid snappers
1540 for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
1541 Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
1542 grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
1543 *nv.gridtoleranceunit,
1544 px));
1545 }
1547 nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
1548 *nv.guidetoleranceunit,
1549 px));
1550 nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
1551 *nv.objecttoleranceunit,
1552 px));
1553 }
1556 NR::Matrix SPDesktop::w2d() const
1557 {
1558 return _w2d;
1559 }
1561 NR::Point SPDesktop::w2d(NR::Point const &p) const
1562 {
1563 return p * _w2d;
1564 }
1566 NR::Point SPDesktop::d2w(NR::Point const &p) const
1567 {
1568 return p * _d2w;
1569 }
1571 NR::Matrix SPDesktop::doc2dt() const
1572 {
1573 return _doc2dt;
1574 }
1576 NR::Point SPDesktop::doc2dt(NR::Point const &p) const
1577 {
1578 return p * _doc2dt;
1579 }
1581 NR::Point SPDesktop::dt2doc(NR::Point const &p) const
1582 {
1583 return p / _doc2dt;
1584 }
1587 /**
1588 * Pop event context from desktop's context stack. Never used.
1589 */
1590 // void
1591 // SPDesktop::pop_event_context (unsigned int key)
1592 // {
1593 // SPEventContext *ec = NULL;
1594 //
1595 // if (event_context && event_context->key == key) {
1596 // g_return_if_fail (event_context);
1597 // g_return_if_fail (event_context->next);
1598 // ec = event_context;
1599 // sp_event_context_deactivate (ec);
1600 // event_context = ec->next;
1601 // sp_event_context_activate (event_context);
1602 // _event_context_changed_signal.emit (this, ec);
1603 // }
1604 //
1605 // SPEventContext *ref = event_context;
1606 // while (ref && ref->next && ref->next->key != key)
1607 // ref = ref->next;
1608 //
1609 // if (ref && ref->next) {
1610 // ec = ref->next;
1611 // ref->next = ec->next;
1612 // }
1613 //
1614 // if (ec) {
1615 // sp_event_context_finish (ec);
1616 // g_object_unref (G_OBJECT (ec));
1617 // }
1618 // }
1620 /*
1621 Local Variables:
1622 mode:c++
1623 c-file-style:"stroustrup"
1624 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1625 indent-tabs-mode:nil
1626 fill-column:99
1627 End:
1628 */
1629 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :