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